PAGE 118,121
TITLE VIDEO ---- 01/10/86  VIDEO DISPLAY BIOS
.XLIST
INCLUDE POSTEQU.INC
INCLUDE DSEG.INC
.LIST
CODE	SEGMENT BYTE PUBLIC

	PUBLIC	ACT_DISP_PAGE
	PUBLIC	READ_AC_CURRENT
	PUBLIC	READ_CURSOR
	PUBLIC	READ_DOT
	PUBLIC	READ_LPEN
	PUBLIC	SCROLL_DOWN
	PUBLIC	SCROLL_UP
	PUBLIC	SET_COLOR
	PUBLIC	SET_CPOS
	PUBLIC	SET_CTYPE
	PUBLIC	SET_MODE
	PUBLIC	WRITE_AC_CURRENT
	PUBLIC	WRITE_C_CURRENT
	PUBLIC	WRITE_DOT
	PUBLIC	WRITE_TTY
	PUBLIC	VIDEO_IO_1
	PUBLIC	VIDEO_STATE

	PUBLIC	SET_MODE
	PUBLIC	SET_CTYPE
	PUBLIC	SET_CPOS
	PUBLIC	READ_CURSOR
	PUBLIC	READ_LPEN
	PUBLIC	ACT_DISP_PAGE
	PUBLIC	SCROLL_UP
	PUBLIC	SCROLL_DOWN
	PUBLIC	READ_AC_CURRENT
	PUBLIC	WRITE_AC_CURRENT
	PUBLIC	WRITE_C_CURRENT
	PUBLIC	SET_COLOR
	PUBLIC	WRITE_DOT
	PUBLIC	READ_DOT
	PUBLIC	WRITE_TTY
	PUBLIC	VIDEO_STATE
	PUBLIC	VIDEO_RETURN
	PUBLIC	VIDEO_RETURN
	PUBLIC	VIDEO_RETURN
	PUBLIC	WRITE_STRING

	EXTRN	BEEP:NEAR		; SPEEKER BEEP ROUTINE
	EXTRN	CRT_CHAR_GEN:NEAR	; CHARACTER GENERATOR GRAPHICS TABLE
	EXTRN	DDS:NEAR		; LOAD (DS) WITH DATA SEGMENT SELECTOR
	EXTRN	M5:WORD 		; REGEN BUFFER LENGTH TABLE
	EXTRN	M6:BYTE 		; COLUMNS PER MODE TABLE
	EXTRN	M7:BYTE 		; MODE SET VALUE PER MODE TABLE

;--- INT 10 H ------------------------------------------------------------------
;									       :
; VIDEO_IO								       :
;	THESE ROUTINES PROVIDE THE CRT DISPLAY INTERFACE		       :
;	THE FOLLOWING FUNCTIONS ARE PROVIDED:				       :
;									       :
;    (AH)= 00H	SET MODE (AL) CONTAINS MODE VALUE			       :
;		 (AL) = 00H  40X25 BW MODE (POWER ON DEFAULT)		       :
;		 (AL) = 01H  40X25 COLOR				       :
;		 (AL) = 02H  80X25 BW					       :
;		 (AL) = 03H  80X25 COLOR				       :
;			      GRAPHICS MODES				       :
;		 (AL) = 04H  320X200 COLOR				       :
;		 (AL) = 05H  320X200 BW MODE				       :
;		 (AL) = 06H  640X200 BW MODE				       :
;		 (AL) = 07H   80X25 MONOCHROME (USED INTERNAL TO VIDEO ONLY)   :
;		 *** NOTES -BW MODES OPERATE SAME AS COLOR MODES, BUT COLOR    :
;			    BURST IS NOT ENABLED			       :
;			   -CURSOR IS NOT DISPLAYED IN GRAPHICS MODE	       :
;    (AH)= 01H	SET CURSOR TYPE 					       :
;		 (CH) =  BITS 4-0 = START LINE FOR CURSOR		       :
;			 ** HARDWARE WILL ALWAYS CAUSE BLINK		       :
;			 ** SETTING BIT 5 OR 6 WILL CAUSE ERRATIC BLINKING     :
;			    OR NO CURSOR AT ALL 			       :
;		 (CL) =  BITS 4-0 = END LINE FOR CURSOR 		       :
;    (AH)= 02H	SET CURSOR POSITION					       :
;		 (DH,DL) = ROW,COLUMN  (00H,00H) IS UPPER LEFT		       :
;		 (BH) = PAGE NUMBER (MUST BE 00H FOR GRAPHICS MODES)	       :
;    (AH)= 03H	READ CURSOR POSITION					       :
;		 (BH) = PAGE NUMBER (MUST BE 00H FOR GRAPHICS MODES)	       :
;		 ON EXIT (DH,DL) = ROW,COLUMN OF CURRENT CURSOR 	       :
;			 (CH,CL) = CURSOR MODE CURRENTLY SET		       :
;    (AH)= 04H	READ LIGHT PEN POSITION 				       :
;		 ON EXIT:						       :
;		 (AH) = 00H -- LIGHT PEN SWITCH NOT DOWN/NOT TRIGGERED	       :
;		 (AH) = 01H -- VALID LIGHT PEN VALUE IN REGISTERS	       :
;			 (DH,DL) = ROW,COLUMN OF CHARACTER LP POSITION	       :
;			 (CH) = RASTER LINE (0-199)			       :
;			 (BX) = PIXEL COLUMN (0-319,639)		       :
;    (AH)= 05H	SELECT ACTIVE DISPLAY PAGE (VALID ONLY FOR ALPHA MODES)        :
;		 (AL) = NEW PAGE VALUE (0-7 FOR MODES 0&1, 0-3 FOR MODES 2&3)  :
;    (AH)= 06H	SCROLL ACTIVE PAGE UP					       :
;		 (AL) = NUMBER OF LINES, ( LINES BLANKED AT BOTTOM OF WINDOW ) :
;			 (AL) = 00H MEANS BLANK ENTIRE WINDOW		       :
;		 (CH,CL) = ROW,COLUMN OF UPPER LEFT CORNER OF SCROLL	       :
;		 (DH,DL) = ROW,COLUMN OF LOWER RIGHT CORNER OF SCROLL	       :
;		 (BH) = ATTRIBUTE TO BE USED ON BLANK LINE		       :
;    (AH)= 07H	SCROLL ACTIVE PAGE DOWN 				       :
;		 (AL) = NUMBER OF LINES, INPUT LINES BLANKED AT TOP OF WINDOW  :
;			 (AL) = 00H MEANS BLANK ENTIRE WINDOW		       :
;		 (CH,CL) = ROW,COLUMN OF UPPER LEFT CORNER OF SCROLL	       :
;		 (DH,DL) = ROW,COLUMN OF LOWER RIGHT CORNER OF SCROLL	       :
;		 (BH) = ATTRIBUTE TO BE USED ON BLANK LINE		       :
;									       :
;   CHARACTER HANDLING ROUTINES 					       :
;									       :
;    (AH)= 08H	READ ATTRIBUTE/CHARACTER AT CURRENT CURSOR POSITION	       :
;		 (BH) = DISPLAY PAGE (VALID FOR ALPHA MODES ONLY)	       :
;		 ON EXIT:						       :
;		 (AL) = CHAR READ					       :
;		 (AH) = ATTRIBUTE OF CHARACTER READ (ALPHA MODES ONLY)	       :
;    (AH)= 09H	WRITE ATTRIBUTE/CHARACTER AT CURRENT CURSOR POSITION	       :
;		 (BH) = DISPLAY PAGE (VALID FOR ALPHA MODES ONLY)	       :
;		 (CX) = COUNT OF CHARACTERS TO WRITE			       :
;		 (AL) = CHAR TO WRITE					       :
;		 (BL) = ATTRIBUTE OF CHARACTER (ALPHA)/COLOR OF CHAR (GRAPHICS):
;			 SEE NOTE ON WRITE DOT FOR BIT 7 OF BL = 1.	       :
;    (AH)= 0AH	WRITE CHARACTER ONLY AT CURRENT CURSOR POSITION 	       :
;		 (BH) = DISPLAY PAGE (VALID FOR ALPHA MODES ONLY)	       :
;		 (CX) = COUNT OF CHARACTERS TO WRITE			       :
;		 (AL) = CHAR TO WRITE					       :
;			NOTE: USE FUNCTION (AH)= 09H IN GRAPHICS MODES	       :
;	FOR READ/WRITE CHARACTER INTERFACE WHILE IN GRAPHICS MODE, THE	       :
;		CHARACTERS ARE FORMED FROM A CHARACTER GENERATOR IMAGE	       :
;		MAINTAINED IN THE SYSTEM ROM. ONLY THE 1ST 128 CHARS	       :
;		ARE CONTAINED THERE. TO READ/WRITE THE SECOND 128 CHARS,       :
;		THE USER MUST INITIALIZE THE POINTER AT INTERRUPT 1FH	       :
;		(LOCATION 0007CH) TO POINT TO THE 1K BYTE TABLE CONTAINING     :
;		THE CODE POINTS FOR THE SECOND 128 CHARS (128-255).	       :
;	FOR WRITE CHARACTER INTERFACE IN GRAPHICS MODE, THE REPLICATION FACTOR :
;		CONTAINED IN (CX) ON ENTRY WILL PRODUCE VALID RESULTS ONLY     :
;		FOR CHARACTERS CONTAINED ON THE SAME ROW. CONTINUATION TO      :
;		SUCCEEDING LINES WILL NOT PRODUCE CORRECTLY.		       :
;									       :
;    GRAPHICS INTERFACE 						       :
;									       :
;    (AH)= 0BH	SET COLOR PALETTE					       :
;		 (BH) = PALETTE COLOR ID BEING SET (0-127)		       :
;		 (BL) = COLOR VALUE TO BE USED WITH THAT COLOR ID	       :
;			NOTE: FOR THE CURRENT COLOR CARD, THIS ENTRY POINT HAS :
;				MEANING ONLY FOR 320X200 GRAPHICS.	       :
;			COLOR ID = 0 SELECTS THE BACKGROUND COLOR (0-15)       :
;			COLOR ID = 1 SELECTS THE PALETTE TO BE USED:	       :
;				0 = GREEN(1)/RED(2)/YELLOW(3)		       :
;				1 = CYAN(1)/MAGENTA(2)/WHITE(3) 	       :
;			IN 40X25 OR 80X25 ALPHA MODES, THE VALUE SET FOR       :
;				PALETTE COLOR 0 INDICATES THE BORDER COLOR     :
;				TO BE USED (VALUES 0-31, WHERE 16-31 SELECT    :
;				THE HIGH INTENSITY BACKGROUND SET.	       :
;    (AH)= 0CH	WRITE DOT						       :
;		 (DX) = ROW NUMBER					       :
;		 (CX) = COLUMN NUMBER					       :
;		 (AL) = COLOR VALUE					       :
;			 IF BIT 7 OF AL = 1, THEN THE COLOR VALUE IS EXCLUSIVE :
;			 ORed WITH THE CURRENT CONTENTS OF THE DOT	       :
;    (AH)= 0DH	READ DOT						       :
;		 (DX) = ROW NUMBER					       :
;		 (CX) = COLUMN NUMBER					       :
;		 (AL) = RETURNS THE DOT READ				       :
;									       :
;    ASCII TELETYPE ROUTINE FOR OUTPUT					       :
;									       :
;    (AH)= 0EH	WRITE TELETYPE TO ACTIVE PAGE				       :
;		 (AL) = CHAR TO WRITE					       :
;		 (BL) = FOREGROUND COLOR IN GRAPHICS MODE		       :
;		 NOTE -- SCREEN WIDTH IS CONTROLLED BY PREVIOUS MODE SET       :
;    (AH)= 0FH	CURRENT VIDEO STATE					       :
;		 RETURNS THE CURRENT VIDEO STATE			       :
;		 (AL) = MODE CURRENTLY SET ( SEE (AH)=00H FOR EXPLANATION)     :
;		 (AH) = NUMBER OR CHARACTER COLUMNS ON SCREEN		       :
;		 (BH) = CURRENT ACTIVE DISPLAY PAGE			       :
;    (AH)= 10H	RESERVED						       :
;    (AH)= 11H	RESERVED						       :
;    (AH)= 12H	RESERVED						       :
;    (AH)= 13H	WRITE STRING						       :
;			   ES:BP  -  POINTER T0 STRING TO BE WRITTEN	       :
;			   CX	  -  LENGTH OF CHARACTER STRING TO WRITTEN     :
;			   DX	  -  CURSOR POSITION FOR STRING TO BE WRITTEN  :
;			   BH	  -  PAGE NUMBER			       :
;	       (AL)= 00H  WRITE CHARACTER STRING			       :
;			   BL	  -  ATTRIBUTE				       :
;			   STRING IS  <CHAR,CHAR, ... ,CHAR>		       :
;			   CURSOR NOT MOVED				       :
;	       (AL)= 01H  WRITE CHARACTER STRING AND MOVE CURSOR	       :
;			   BL	  -  ATTRIBUTE				       :
;			   STRING IS  <CHAR,CHAR, ... ,CHAR>		       :
;			   CURSOR MOVED 				       :
;	       (AL)= 02H  WRITE CHARACTER AND ATTRIBUTE STRING		       :
;				 (VALID FOR ALPHA MODES ONLY)		       :
;			   STRING IS  <CHAR,ATTR,CHAR,ATTR ..  ,CHAR,ATTR>     :
;			   CURSOR IS NOT MOVED				       :
;	       (AL)= 03H  WRITE CHARACTER AND ATTRIBUTE STRING AND MOVE CURSOR :
;				 (VALID FOR ALPHA MODES ONLY)		       :
;			   STRING IS  <CHAR,ATTR,CHAR,ATTR ..  ,CHAR,ATTR>     :
;			   CURSOR IS MOVED				       :
;		  NOTE:  CARRIAGE RETURN, LINE FEED, BACKSPACE, AND BELL ARE   :
;			 TREATED AS COMMANDS RATHER THAN PRINTABLE CHARACTERS. :
;									       :
;	BX,CX,DX,SI,DI,BP,SP,DS,ES,SS PRESERVED DURING CALLS EXCEPT FOR        :
;	BX,CX,DX RETURN VALUES ON FUNCTIONS 03H,04H,0DH AND 0FH. ON ALL CALLS  :
;	AX IS MODIFIED. 						       :
;-------------------------------------------------------------------------------

	ASSUME	CS:CODE,DS:DATA,ES:NOTHING

M1	DW	OFFSET	SET_MODE	; TABLE OF ROUTINES WITHIN VIDEO I/O
	DW	OFFSET	SET_CTYPE
	DW	OFFSET	SET_CPOS
	DW	OFFSET	READ_CURSOR
	DW	OFFSET	READ_LPEN
	DW	OFFSET	ACT_DISP_PAGE
	DW	OFFSET	SCROLL_UP
	DW	OFFSET	SCROLL_DOWN
	DW	OFFSET	READ_AC_CURRENT
	DW	OFFSET	WRITE_AC_CURRENT
	DW	OFFSET	WRITE_C_CURRENT
	DW	OFFSET	SET_COLOR
	DW	OFFSET	WRITE_DOT
	DW	OFFSET	READ_DOT
	DW	OFFSET	WRITE_TTY
	DW	OFFSET	VIDEO_STATE
	DW	OFFSET	VIDEO_RETURN	; RESERVED
	DW	OFFSET	VIDEO_RETURN	; RESERVED
	DW	OFFSET	VIDEO_RETURN	; RESERVED
	DW	OFFSET	WRITE_STRING	; CASE 19H, WRITE STRING
M1L	EQU	$-M1

VIDEO_IO_1	PROC	NEAR		;	ENTRY POINT FOR ORG 0F065H
	STI				; INTERRUPTS BACK ON
	CLD				; SET DIRECTION FORWARD
	CMP	AH,M1L/2		; TEST FOR WITHIN TABLE RANGE
	JNB	M4			; BRANCH TO EXIT IF NOT A VALID COMMAND

	PUSH	ES
	PUSH	DS			; SAVE WORK AND PARAMETER REGISTERS
	PUSH	DX
	PUSH	CX
	PUSH	BX
	PUSH	SI
	PUSH	DI
	PUSH	BP
	MOV	SI,DATA 		; POINT DS: TO DATA SEGMENT
	MOV	DS,SI
	MOV	SI,AX			; SAVE COMMAND/DATA INTO (SI) REGISTER
	MOV	AL,BYTE PTR @EQUIP_FLAG ; GET EQUIPMENT FLAG VIDEO BITS
	AND	AL,30H			; ISOLATE CRT SWITCHES
	CMP	AL,30H			; IS SETTING FOR MONOCHROME CARD?
	MOV	DI,0B800H		; GET SEGMENT FOR COLOR CARD
	JNE	M2			; SKIP IF NOT BW CARD
	MOV	DI,0B000H		; ELSE GET SEGMENT FOR MONOCHROME CARD
M2:
	MOV	ES,DI			; SET UP TO POINT AT VIDEO MEMORY AREAS
	MOV	AL,AH			; PLACE COMMAND IN LOW BYTE OF (AX)
	CBW				; AND FORM BYTE OFFSET WITH COMMAND
	SAL	AX,1			; TIMES 2 FOR WORD TABLE LOOKUP
	XCHG	SI,AX			; MOVE OFFSET INTO LOOK UP REGISTER (SI)
					;  AND RESTORE COMMAND/DATA INTO (AX)
	MOV	AH,@CRT_MODE		; MOVE CURRENT MODE INTO (AH) REGISTER

	JMP	WORD PTR CS:[SI+OFFSET M1]	; GO TO SELECTED FUNCTION

M4:					;	COMMAND NOT VALID
	IRET				; DO NOTHING IF NOT IN VALID RANGE
VIDEO_IO_1	ENDP
;--------------------------------------------------------
; SET_MODE						:
;	THIS ROUTINE INITIALIZES THE ATTACHMENT TO	:
;	THE SELECTED MODE.  THE SCREEN IS BLANKED.	:
; INPUT 						:
;	@EQUIP_FLAG BITS 5-4 = MODE/WIDTH		:
;	    11 = MONOCHROME (FORCES MODE 7)		:
;	    01 = COLOR ADAPTER 40x25 (MODE 0 DEFAULT)	:
;	    10 = COLOR ADAPTER 80x25 (MODE 2 DEFAULT)	:
;	  (AL) = COLOR MODE REQUESTED ( RANGE  0 - 6 )	:
; OUTPUT						:
;	NONE						:
;--------------------------------------------------------
SET_MODE	PROC	NEAR
	MOV	DX,03D4H		; ADDRESS OF COLOR CARD
	MOV	DI,@EQUIP_FLAG		; GET EQUIPMENT FLAGS SETTING
	AND	DI,30H			; ISOLATE CRT SWITCHES
	CMP	DI,30H			; IS BW CARD INSTALLED AS PRIMARY
	JNE	M8C			; SKIP AND CHECK IF COLOR
	MOV	AL,7			; ELSE INDICATE INTERNAL BW CARD MODE
	MOV	DL,0B4H 		; SET ADDRESS OF BW (MONOCHROME) CARD
	JMP	SHORT M8		; CONTINUE WITH FORCED MODE 7
M8C:
	CMP	AL,7			; CHECK FOR VALID COLOR MODES 0-6
	JB	M8			; CONTINUE IF BELOW MODE 7
	MOV	AL,0			; FORCE DEFAULT 40x25 BW MODE
	CMP	DI,20H			; CHECK FOR @EQUIP_FLAG AT 80x25 BW
	JE	M8			; CONTINUE WITH MODE 0 IF NOT
	MOV	AL,2			; ELSE FORCE MODE 2
M8:
	MOV	@CRT_MODE,AL		; SAVE MODE IN GLOBAL VARIABLE
	MOV	@ADDR_6845,DX		; SAVE ADDRESS OF BASE
	MOV	@ROWS,25-1		; INITIALIZE DEFAULT ROW COUNT OF 25
	PUSH	DS			; SAVE POINTER TO DATA SEGMENT
	PUSH	AX			; SAVE MODE NUMBER (AL)
	CBW				; CLEAR HIGH BYTE OF MODE
	MOV	SI,AX			; SET TABLE POINTER, INDEXED BY MODE
	MOV	AL,CS:[SI + OFFSET M7]	; GET THE MODE SET VALUE FROM TABLE
	MOV	@CRT_MODE_SET,AL	; SAVE THE MODE SET VALUE
	AND	AL,037H 		; VIDEO OFF, SAVE HIGH RESOLUTION SIT
	PUSH	DX			; SAVE OUTPUT PORT VALUE
	ADD	DX,4			; POINT TO CONTROL REGISTER
	OUT	DX,AL			; RESET VIDEO TO OFF TO SUPPRESS ROLLING
	POP	DX			; BACK TO BASE REGISTER
	ASSUME	DS:ABS0
	SUB	BX,BX			; SET UP FOR ABS0 SECMENT
	MOV	DS,BX			; ESTABLISH VECTOR TABLE ADDRESSING
	LDS	BX,@PARM_PTR		; GET POINTER TO VIDEO PARMS
	ASSUME	DS:CODE
	POP	AX			; RECOVER MODE NUMBER IN (AL)
	MOV	CX,16			; LENGTH OF EACH ROW OF TABLE
	CMP	AL,2			; DETERMINE WHICH ONE TO USE
	JC	M9			; MODE IS 0 OR 1
	ADD	BX,CX			; NEXT ROW OF INITIALIZATION TABLE
	CMP	AL,4
	JC	M9			; MODE IS 2 OR 3
	ADD	BX,CX			; MOVE TO GRAPHICS ROW OF INIT_TABLE
	CMP	AL,7
	JC	M9			; MODE IS 4,5, OR 6
	ADD	BX,CX			; MOVE TO BW CARD ROW OF INIT_TABLE

;-----	BX POINTS TO CORRECT ROW OF INITIALIZATION TABLE

M9:					; OUT_INIT
	PUSH	AX			; SAVE MODE IN (AL)
	MOV	AX,[BX+10]		; GET THE CURSOR MODE FROM THE TABLE
	XCHG	AH,AL			; PUT CURSOR MODE IN CORRECT POSITION
	PUSH	DS			; SAVE TABLE SEGMENT POINTER
	ASSUME	DS:DATA
	CALL	DDS			; POINT DS TO DATA SEGMENT
	MOV	@CURSOR_MODE,AX 	; PLACE INTO BIOS DATA SAVE AREA
	ASSUME	DS:CODE
	POP	DS			; RESTORE THE TABLE SEGMENT POINTER
	XOR	AH,AH			; AH IS REGISTER NUMBER DURING LOOP

;-----	LOOP THROUGH TABLE, OUTPUTTING REGISTER ADDRESS, THEN VALUE FROM TABLE

M10:					;  INITIALIZATION LOOP
	MOV	AL,AH			; GET 6845 REGISTER NUMBER
	OUT	DX,AL
	INC	DX			; POINT TO DATA PORT
	INC	AH			; NEXT REGISTER VALUE
	MOV	AL,[BX] 		; GET TABLE VALUE
	OUT	DX,AL			; OUT TO CHIP
	INC	BX			; NEXT IN TABLE
	DEC	DX			; BACK TO POINTER REGISTER
	LOOP	M10			; DO THE WHOLE TABLE
	POP	AX			; GET MODE BACK INTO (AL)
	POP	DS			; RECOVER SEGMENT VALUE
	ASSUME	DS:DATA

;-----	FILL REGEN AREA WITH BLANK

	XOR	DI,DI			; SET UP POINTER FOR REGEN
	MOV	@CRT_START,DI		; START ADDRESS SAVED IN GLOBAL
	MOV	@ACTIVE_PAGE,0		; SET PAGE VALUE
	MOV	CX,8192 		; NUMBER OF WORDS IN COLOR CARD
	CMP	AL,4			; TEST FOR GRAPHICS
	JC	M12			; NO_GRAPHICS_INIT
	CMP	AL,7			; TEST FOR BW CARD
	JE	M11			; BW_CARD_INIT
	XOR	AX,AX			; FILL FOR GRAPHICS MODE
	JMP	SHORT M13		; CLEAR BUFFER
M11:					; BW_CARD_INIT
	MOV	CH,08H			; BUFFER SIZE ON BW CARD (2048)
M12:					; NO_GRAPHICS_INIT
	MOV	AX,' '+7*H              ; FILL CHAR FOR ALPHA + ATTRIBUTE
M13:					; CLEAR BUFFER
	REP	STOSW			; FILL THE REGEN BUFFER WITH BLANKS

;-----	ENABLE VIDEO AND CORRECT PORT SETTING

	MOV	DX,@ADDR_6845		; PREPARE TO OUTPUT TO VIDEO ENABLE PORT
	ADD	DX,4			; POINT TO THE MODE CONTROL REGISTER
	MOV	AL,@CRT_MODE_SET	; GET THE MODE SET VALUE
	OUT	DX,AL			; SET VIDEO ENABLE PORT

;-----	DETERMINE NUMBER OF COLUMNS, BOTH FOR ENTIRE DISPLAY
;-----	AND THE NUMBER TO BE USED FOR TTY INTERFACE

	MOV	AL,CS:[SI + OFFSET M6]	; GET NUMBER OF COLUMNS ON THIS SCREEN
	CBW				; CLEAR HIGH BYTE
	MOV	@CRT_COLS,AX		; INITIALIZE NUMBER OF COLUMNS COUNT

;-----	SET CURSOR POSITIONS

	AND	SI,000EH		; WORD OFFSET INTO CLEAR LENGTH TABLE
	MOV	AX,CS:[SI + OFFSET M5]	; LENGTH TO CLEAR
	MOV	@CRT_LEN,AX		; SAVE LENGTH OF CRT -- NOT USED FOR BW
	MOV	CX,8			; CLEAR ALL CURSOR POSITIONS
	MOV	DI,OFFSET @CURSOR_POSN
	PUSH	DS			; ESTABLISH SEGMENT
	POP	ES			;   ADDRESSING
	XOR	AX,AX
	REP	STOSW			; FILL WITH ZEROES

;-----	SET UP OVERSCAN REGISTER

	INC	DX			; SET OVERSCAN PORT TO A DEFAULT
	MOV	AL,30H			; 30H VALUE FOR ALL MODES EXCEPT 640X200
	CMP	@CRT_MODE,6		; SEE IF THE MODE IS 640X200 BW
	JNZ	M14			; IF NOT 640X200, THEN GO TO REGULAR
	MOV	AL,3FH			; IF IT IS 640X200, THEN PUT IN 3FH
M14:
	OUT	DX,AL			; OUTPUT THE CORRECT VALUE TO 3D9 PORT
	MOV	@CRT_PALETTE,AL 	; SAVE THE VALUE FOR FUTURE USE

;-----	NORMAL RETURN FROM ALL VIDEO RETURNS

VIDEO_RETURN:
	POP	BP
	POP	DI
	POP	SI
	POP	BX
M15:					; VIDEO_RETURN_C
	POP	CX
	POP	DX
	POP	DS
	POP	ES			; RECOVER SEGMENTS
	IRET				; ALL DONE
SET_MODE	ENDP
;---------------------------------------------------
; SET_CTYPE
;	THIS ROUTINE SETS THE CURSOR VALUE
; INPUT
;	(CX) HAS CURSOR VALUE CH-START LINE, CL-STOP LINE
; OUTPUT
;	NONE
;----------------------------------------------------
SET_CTYPE	PROC	NEAR
	MOV	AH,10			; 6845 REGISTER FOR CURSOR SET
	MOV	@CURSOR_MODE,CX 	; SAVE IN DATA AREA
	CALL	M16			; OUTPUT CX REGISTER
	JMP	VIDEO_RETURN

;-----	THIS ROUTINE OUTPUTS THE CX REGISTER TO THE 6845 REGISTERS NAMED IN (AH)


M16:
	MOV	DX,@ADDR_6845		; ADDRESS REGISTER
	MOV	AL,AH			; GET VALUE
	OUT	DX,AL			; REGISTER SET
	INC	DX			; DATA REGISTER
	MOV	AL,CH			; DATA
	OUT	DX,AL
	DEC	DX
	MOV	AL,AH
	INC	AL			; POINT TO OTHER DATA REGISTER
	OUT	DX,AL			; SET FOR SECOND REGISTER
	INC	DX
	MOV	AL,CL			; SECOND DATA VALUE
	OUT	DX,AL
	RET				; ALL DONE
SET_CTYPE	ENDP

;--------------------------------------------
; SET_CPOS
;	THIS ROUTINE SETS THE CURRENT CURSOR POSITION TO THE
;	NEW X-Y VALUES PASSED
; INPUT
;	DX - ROW,COLUMN OF NEW CURSOR
;	BH - DISPLAY PAGE OF CURSOR
; OUTPUT
;	CURSOR IS SET AT 6845 IF DISPLAY PAGE IS CURRENT DISPLAY
;---------------------------------------------
SET_CPOS	PROC	NEAR
	MOV	AL,BH			; MOVE PAGE NUMBER TO WORK REGISTER
	CBW				; CONVERT PAGE TO WORD VALUE
	SAL	AX,1			; WORD OFFSET
	XCHG	AX,SI			; USE INDEX REGISTER
	MOV	[SI+OFFSET @CURSOR_POSN],DX	; SAVE THE POINTER
	CMP	@ACTIVE_PAGE,BH
	JNZ	M17			; SET_CPOS_RETURN
	MOV	AX,DX			; GET ROW/COLUMN TO AX
	CALL	M18			; CURSOR SET
M17:					; SET_CPOS_RETURN
	JMP	VIDEO_RETURN
SET_CPOS	ENDP

;-----	SET CURSOR POSITION, AX HAS ROW/COLUMN FOR CURSOR

M18	PROC	NEAR
	CALL	POSITION		; DETERMINE LOCATION IN REGEN BUFFER
	MOV	CX,AX
	ADD	CX,@CRT_START		; ADD IN THE START ADDRESS FOR THIS PAGE
	SAR	CX,1			; DIVIDE BY 2 FOR CHAR ONLY COUNT
	MOV	AH,14			; REGISTER NUMBER FOR CURSOR
	CALL	M16			; OUTPUT THE VALUE TO THE 6845
	RET
M18	ENDP
;----------------------------------------------------
; READ_CURSOR
;	THIS ROUTINE READS THE CURRENT CURSOR VALUE FROM THE
;	6845, FORMATS IT, AND SENDS IT BACK TO THE CALLER
; INPUT
;	BH - PAGE OF CURSOR
; OUTPUT
;	DX - ROW, COLUMN OF THE CURRENT CURSOR POSITION
;	CX - CURRENT CURSOR MODE
;-------------------------------------------
READ_CURSOR	PROC	NEAR
	MOV	BL,BH
	XOR	BH,BH
	SAL	BX,1			; WORD OFFSET
	MOV	DX,[BX+OFFSET @CURSOR_POSN]
	MOV	CX,@CURSOR_MODE
	POP	BP
	POP	DI
	POP	SI
	POP	BX
	POP	AX			; DISCARD SAVED CX AND DX
	POP	AX
	POP	DS
	POP	ES
	IRET
READ_CURSOR	ENDP
;------------------------------------------------
; ACT_DISP_PAGE
;	THIS ROUTINE SETS THE ACTIVE DISPLAY PAGE, ALLOWING
;	THE FULL USE OF THE MEMORY SET ASIDE FOR THE VIDEO ATTACHMENT
; INPUT
;	AL HAS THE NEW ACTIVE DISPLAY PAGE
; OUTPUT
;	THE 6845 IS RESET TO DISPLAY THAT PAGE
;-------------------------------------------------
ACT_DISP_PAGE	PROC	NEAR
	MOV	@ACTIVE_PAGE,AL 	; SAVE ACTIVE PAGE VALUE
	CBW				; CONVERT AL TO WORD
	PUSH	AX			; SAVE PAGE VALUE
	MUL	WORD PTR @CRT_LEN	; DISPLAY PAGE TIMES REGEN LENGTH
	MOV	@CRT_START,AX		; SAVE START ADDRESS FOR LATER
	MOV	CX,AX			; START ADDRESS TO CX
	SAR	CX,1			; DIVIDE BY 2 FOR 6845 HANDLING
	MOV	AH,12			; 6845 REGISTER FOR START ADDRESS
	CALL	M16
	POP	BX			; RECOVER PAGE VALUE
	SAL	BX,1			; *2 FOR WORD OFFSET
	MOV	AX,[BX + OFFSET @CURSOR_POSN]	; GET CURSOR FOR THIS PAGE
	CALL	M18			; SET THE CURSOR POSITION
	JMP	VIDEO_RETURN
ACT_DISP_PAGE	ENDP
;---------------------------------------------
; SET_COLOR
;	THIS ROUTINE SILL ESTABLISH THE BACKGROUND COLOR, THE OVERSCAN COLOR,
;	AND THE FOREGROUND COLOR SET FOR MEDIUM RESOLUTION GRAPHICS
; INPUT
;	(BH) HAS COLOR ID
;		IF BH=0, THE BACKGROUND COLOR VALUE IS SET
;			FROM THE LOW BITS OF BL (0-31)
;		IF BH=1, THE PALETTE SELECTION IS MADE
;			BASED ON THE LOW BIT OF BL:
;				0 = GREEN, RED, YELLOW FOR COLORS 1,2,3
;				1 = BLUE, CYAN, MAGENTA FOR COLORS 1,2,3
;	(BL) HAS THE COLOR VALUE TO BE USED
; OUTPUT
;	THE COLOR SELECTION IS UPDATED
;----------------------------------------------
SET_COLOR	PROC	NEAR
	MOV	DX,@ADDR_6845		; I/O PORT FOR PALETTE
	ADD	DX,5			; OVERSCAN PORT
	MOV	AL,@CRT_PALETTE 	; GET THE CURRENT PALETTE VALUE
	OR	BH,BH			; IS THIS COLOR 0?
	JNZ	M20			; OUTPUT COLOR 1

;-----	HANDLE COLOR 0 BY SETTING THE BACKGROUND COLOR

	AND	AL,0E0H 		; TURN OFF LOW 5 BITS OF CURRENT
	AND	BL,01FH 		; TURN OFF HIGH 3 BITS OF INPUT VALUE
	OR	AL,BL			; PUT VALUE INTO REGISTER
M19:					; OUTPUT THE PALETTE
	OUT	DX,AL			; OUTPUT COLOR SELECTION TO 3D9 PORT
	MOV	@CRT_PALETTE,AL 	; SAVE THE COLOR VALUE
	JMP	VIDEO_RETURN

;-----	HANDLE COLOR 1 BY SELECTING THE PALETTE TO BE USED

M20:
	AND	AL,0DFH 		; TURN OFF PALETTE SELECT BIT
	SHR	BL,1			; TEST THE LOW ORDER BIT OF BL
	JNC	M19			; ALREADY DONE
	OR	AL,20H			; TURN ON PALETTE SELECT BIT
	JMP	M19			; GO DO IT
SET_COLOR	ENDP
;-------------------------------------------------
; VIDEO STATE
;  RETURNS THE CURRENT VIDEO STATE IN AX
;  AH = NUMBER OF COLUMNS ON THE SCREEN
;  AL = CURRENT VIDEO MODE
;  BH = CURRENT ACTIVE PAGE
;-------------------------------------------------
VIDEO_STATE	PROC	NEAR
	MOV	AH,BYTE PTR @CRT_COLS	; GET NUMBER OF COLUMNS
	MOV	AL,@CRT_MODE		; CURRENT MODE
	MOV	BH,@ACTIVE_PAGE 	; GET CURRENT ACTIVE PAGE
	POP	BP			; RECOVER REGISTERS
	POP	DI
	POP	SI
	POP	CX			; DISCARD SAVED BX
	JMP	M15			; RETURN TO CALLER
VIDEO_STATE	ENDP
;----------------------------------------
; POSITION
;	THIS SERVICE ROUTINE CALCULATES THE REGEN BUFFER ADDRESS
;	OF A CHARACTER IN THE ALPHA MODE
; INPUT
;	AX = ROW, COLUMN POSITION
; OUTPUT
;	AX = OFFSET OF CHAR POSITION IN REGEN BUFFER
;----------------------------------------
POSITION	PROC	NEAR
	PUSH	BX			; SAVE REGISTER
	XCHG	BX,AX			; SAVE ROW/COLUMN POSITION IN (BX)
	MOV	AL,BYTE PTR @CRT_COLS	; GET COLUMNS PER ROW COUNT
	MUL	BH			; DETERMINE BYTES TO ROW
	XOR	BH,BH
	ADD	AX,BX			; ADD IN COLUMN VALUE
	SAL	AX,1			; * 2 FOR ATTRIBUTE BYTES
	POP	BX
	RET
POSITION	ENDP
;------------------------------------------
; SCROLL UP
;	THIS ROUTINE MOVES A BLOCK OF CHARACTERS UP
;	ON THE SCREEN
; INPUT
;	(AH) = CURRENT CRT MODE
;	(AL) = NUMBER OF ROWS TO SCROLL
;	(CX) = ROW/COLUMN OF UPPER LEFT CORNER
;	(DX) = ROW/COLUMN OF LOWER RIGHT CORNER
;	(BH) = ATTRIBUTE TO BE USED ON BLANKED LINE
;	(DS) = DATA SEGMENT
;	(ES) = REGEN BUFFER SEGMENT
; OUTPUT
;	NONE -- THE REGEN BUFFER IS MODIFIED
;-------------------------------------------
	ASSUME	DS:DATA,ES:DATA
SCROLL_UP	PROC	NEAR

	CALL	TEST_LINE_COUNT
	CMP	AH,4			; TEST FOR GRAPHICS MODE
	JC	N1			; HANDLE SEPARATELY
	CMP	AH,7			; TEST FOR BW CARD
	JE	N1
	JMP	GRAPHICS_UP
N1:					; UP_CONTINUE
	PUSH	BX			; SAVE FILL ATTRIBUTE IN BH
	MOV	AX,CX			; UPPER LEFT POSITION
	CALL	SCROLL_POSITION 	; DO SETUP FOR SCROLL
	JZ	N7			; BLANK_FIELD
	ADD	SI,AX			; FROM ADDRESS
	MOV	AH,DH			; # ROWS IN BLOCK
	SUB	AH,BL			; # ROWS TO BE MOVED
N2:					; ROW_LOOP
	CALL	N10			; MOVE ONE ROW
	ADD	SI,BP
	ADD	DI,BP			; POINT TO NEXT LINE IN BLOCK
	DEC	AH			; COUNT OF LINES TO MOVE
	JNZ	N2			; ROW_LOOP
N3:					; CLEAR_ENTRY
	POP	AX			; RECOVER ATTRIBUTE IN AH
	MOV	AL,' '                  ; FILL WITH BLANKS
N4:					; CLEAR_LOOP
	CALL	N11			; CLEAR THE ROW
	ADD	DI,BP			; POINT TO NEXT LINE
	DEC	BL			; COUNTER OF LINES TO SCROLL
	JNZ	N4			; CLEAR_LOOP
N5:					; SCROLL_END
	CALL	DDS
	CMP	@CRT_MODE,7		; IS THIS THE BLACK AND WHITE CARD
	JE	N6			; IF SO, SKIP THE MODE RESET
	MOV	AL,@CRT_MODE_SET	; GET THE VALUE OF THE MODE SET
	MOV	DX,03D8H		; ALWAYS SET COLOR CARD PORT
	OUT	DX,AL
N6:					; VIDEO_RET_HERE
	JMP	VIDEO_RETURN
N7:					; BLANK_FIELD
	MOV	BL,DH			; GET ROW COUNT
	JMP	N3			; GO CLEAR THAT AREA
SCROLL_UP	ENDP

; ----- HANDLE COMMON SCROLL SET UP HERE

SCROLL_POSITION PROC	NEAR
	CALL	POSITION		; CONVERT TO REGEN POINTER
	ADD	AX,@CRT_START		; OFFSET OF ACTIVE PAGE
	MOV	DI,AX			; TO ADDRESS FOR SCROLL
	MOV	SI,AX			; FROM ADDRESS FOR SCROLL
	SUB	DX,CX			; DX = #ROWS, #COLS IN BLOCK
	INC	DH
	INC	DL			; INCREMENT FOR 0 ORIGIN
	XOR	CH,CH			; SET HIGH BYTE OF COUNT TO ZERO
	MOV	BP,@CRT_COLS		; GET NUMBER OF COLUMNS IN DISPLAY
	ADD	BP,BP			; TIMES 2 FOR ATTRIBUTE BYTE
	MOV	AL,BYTE PTR @CRT_COLS	; GET CHARACTERS PER LINE COUNT
	MUL	BL			; DETERMINE OFFSET TO FROM ADDRESS
	ADD	AX,AX			; *2 FOR ATTRIBUTE BYTE
	PUSH	AX			; SAVE LINE COUNT
	MOV	AL,@CRT_MODE		; GET CURRENT MODE
	PUSH	ES			; ESTABLISH ADDRESSING TO REGEN BUFFER
	POP	DS			;  FOR BOTH POINTERS
	CMP	AL,2			; TEST FOR COLOR CARD SPECIAL CASES HERE
	JB	N9			; HAVE TO HANDLE 80X25 SEPARATELY
	CMP	AL,3
	JA	N9
;-----					; 80X25 COLOR CARD SCROLL
	PUSH	DX
	MOV	DX,3DAH 		; GUARANTEED TO BE COLOR CARD HERE
N8:					; WAIT_DISP_ENABLE
	IN	AL,DX			; GET PORT
	TEST	AL,RVRT 		; WAIT FOR VERTICAL RETRACE
	JZ	N8			; WAIT_DISP_ENABLE
	MOV	AL,25H
	MOV	DL,0D8H 		; ADDRESS CONTROL PORT
	OUT	DX,AL			; TURN OFF VIDEO DURING VERTICAL RETRACE
	POP	DX
N9:
	POP	AX			; RESTORE LINE COUNT
	OR	BL,BL			; 0 SCROLL MEANS BLANK FIELD
	RET				; RETURN WITH FLAGS SET
SCROLL_POSITION ENDP

;-----	MOVE_ROW
N10	PROC	NEAR
	MOV	CL,DL			; GET # OF COLS TO MOVE
	PUSH	SI
	PUSH	DI			; SAVE START ADDRESS
	REP	MOVSW			; MOVE THAT LINE ON SCREEN
	POP	DI
	POP	SI			; RECOVER ADDRESSES
	RET
N10	ENDP

;-----	CLEAR_ROW
N11	PROC	NEAR
	MOV	CL,DL			; GET # COLUMNS TO CLEAR
	PUSH	DI
	REP	STOSW			; STORE THE FILL CHARACTER
	POP	DI
	RET
N11	ENDP
;----------------------------------------
; SCROLL_DOWN
;	THIS ROUTINE MOVES THE CHARACTERS WITHIN A DEFINED
;	BLOCK DOWN ON THE SCREEN, FILLING THE TOP LINES
;	WITH A DEFINED CHARACTER
; INPUT
;	(AH) = CURRENT CRT MODE
;	(AL) = NUMBER OF LINES TO SCROLL
;	(CX) = UPPER LEFT CORNER OF REGION
;	(DX) = LOWER RIGHT CORNER OF REGION
;	(BH) = FILL CHARACTER
;	(DS) = DATA SEGMENT
;	(ES) = REGEN SEGMENT
; OUTPUT
;	NONE -- SCREEN IS SCROLLED
;----------------------------------------
SCROLL_DOWN	PROC	NEAR
	STD				; DIRECTION FOR SCROLL DOWN
	CALL	TEST_LINE_COUNT
	CMP	AH,4			; TEST FOR GRAPHICS
	JC	N12
	CMP	AH,7			; TEST FOR BW CARD
	JE	N12
	JMP	GRAPHICS_DOWN
N12:					; CONTINUE_DOWN
	PUSH	BX			; SAVE ATTRIBUTE IN BH
	MOV	AX,DX			; LOWER RIGHT CORNER
	CALL	SCROLL_POSITION 	; GET REGEN LOCATION
	JZ	N16
	SUB	SI,AX			; SI IS FROM ADDRESS
	MOV	AH,DH			; GET TOTAL # ROWS
	SUB	AH,BL			; COUNT TO MOVE IN SCROLL
N13:
	CALL	N10			; MOVE ONE ROW
	SUB	SI,BP
	SUB	DI,BP
	DEC	AH
	JNZ	N13
N14:
	POP	AX			; RECOVER ATTRIBUTE IN AH
	MOV	AL,' '
N15:
	CALL	N11			; CLEAR ONE ROW
	SUB	DI,BP			; GO TO NEXT ROW
	DEC	BL
	JNZ	N15
	JMP	N5			; SCROLL_END
N16:
	MOV	BL,DH
	JMP	N14
SCROLL_DOWN	ENDP
PAGE
;-----	IF  AMOUNT OF LINES TO BE SCROLLED = AMOUNT OF LINES IN WINDOW
;-----	 THEN  ADJUST AL; ELSE	RETURN;

TEST_LINE_COUNT PROC	NEAR

	MOV	BL,AL			; SAVE LINE COUNT IN BL
	OR	AL,AL			; TEST IF AL IS ALREADY ZERO
	JZ	BL_SET			; IF IT IS THEN RETURN...
	PUSH	AX			; SAVE AX
	MOV	AL,DH			; SUBTRACT LOWER ROW FROM UPPER ROW
	SUB	AL,CH
	INC	AL			; ADJUST DIFFERENCE BY 1
	CMP	AL,BL			; LINE COUNT = AMOUNT OF ROWS IN WINDOW?
	POP	AX			; RESTORE AX
	JNE	BL_SET			; IF NOT THEN WE'RE ALL SET
	SUB	BL,BL			; OTHERWISE SET BL TO ZERO
BL_SET:
	RET				; RETURN
TEST_LINE_COUNT ENDP

;-------------------------------------------------------------------------------
; READ_AC_CURRENT							       :
;	THIS ROUTINE READS THE ATTRIBUTE AND CHARACTER AT THE CURRENT	       :
;	CURSOR POSITION AND RETURNS THEM TO THE CALLER			       :
; INPUT 								       :
;	(AH) = CURRENT CRT MODE 					       :
;	(BH) = DISPLAY PAGE ( ALPHA MODES ONLY )			       :
;	(DS) = DATA SEGMENT						       :
;	(ES) = REGEN SEGMENT						       :
; OUTPUT								       :
;	(AL) = CHARACTER READ						       :
;	(AH) = ATTRIBUTE READ						       :
;-------------------------------------------------------------------------------
	ASSUME	DS:DATA,ES:DATA

READ_AC_CURRENT PROC	NEAR
	CMP	AH,4			; IS THIS GRAPHICS
	JC	P10

	CMP	AH,7			; IS THIS BW CARD
	JE	P10

	JMP	GRAPHICS_READ
P10:					;  READ_AC_CONTINUE
	CALL	FIND_POSITION		; GET REGEN LOCATION AND PORT ADDRESS
	MOV	SI,DI			; ESTABLISH ADDRESSING IN SI
	PUSH	ES			; GET REGEN SEGMENT FOR QUICK ACCESS
	POP	DS

;-----	WAIT FOR HORIZONTAL RETRACE OR VERTICAL RETRACE IF COLOR 80

	OR	BL,BL			; CHECK MODE FLAG FOR COLOR CARD IN 80
	JNZ	P13			; ELSE SKIP RETRACE WAIT - DO FAST READ
P11:					;  WAIT FOR HORZ RETRACE LOW OR VERTICAL
	STI				; ENABLE INTERRUPTS FIRST
	NOP				; ALLOW FOR SMALL INTERRUPT WINDOW
	CLI				; BLOCK INTERRUPTS FOR SINGLE LOOP
	IN	AL,DX			; GET STATUS FROM THE ADAPTER
	TEST	AL,RHRZ 		; IS HORIZONTAL RETRACE LOW
	JNZ	P11			; WAIT UNTIL IT IS
P12:					;  NOW WAIT FOR EITHER RETRACE HIGH
	IN	AL,DX			; GET STATUS
	TEST	AL,RVRT+RHRZ		; IS HORIZONTAL OR VERTICAL RETRACE HIGH
	JZ	P12			; WAIT UNTIL EITHER IS ACTIVE
P13:
	LODSW				; GET THE CHARACTER AND ATTRIBUTE
	JMP	VIDEO_RETURN		;  EXIT WITH (AX)

READ_AC_CURRENT ENDP


FIND_POSITION	PROC	NEAR		;	SETUP FOR BUFFER READ OR WRITE
	XCHG	AH,BL			; SWAP MODE TYPE WITH ATTRIBUTE
	MOV	BP,AX			; SAVE CHARACTER/ATTR IN (BP) REGISTER
	SUB	BL,2			; CONVERT DISPLAY MODE TYPE TO A
	SHR	BL,1			;  ZERO VALUE FOR COLOR IN 80 COLUMN
	MOV	AL,BH			; MOVE DISPLAY PAGE TO LOW BYTE
	CBW				; CLEAR HIGH BYTE FOR BYTE OFFSET
	MOV	DI,AX			; MOVE DISPLAY PAGE (COUNT) TO WORK REG
	SAL	DI,1			; TIMES 2 FOR WORD OFFSET
	MOV	DX,[DI+OFFSET @CURSOR_POSN]	; GET ROW/COLUMN OF THAT PAGE
	JZ	P21			; SKIP BUFFER ADJUSTMENT IF PAGE ZERO

	XOR	DI,DI			; ELSE SET BUFFER START ADDRESS TO ZERO
P20:
	ADD	DI,@CRT_LEN		; ADD LENGTH OF BUFFER FOR ONE PAGE
	DEC	AX			; DECREMENT PAGE COUNT
	JNZ	P20			; LOOP TILL PAGE COUNT EXHAUSTED

P21:					; DETERMINE LOCATION IN REGEN IN PAGE
	MOV	AL,BYTE PTR @CRT_COLS	; GET COLUMNS PER ROW COUNT
	MUL	DH			; DETERMINE BYTES TO ROW
	XOR	DH,DH			;
	ADD	AX,DX			; ADD IN COLUMN VALUE
	SAL	AX,1			; * 2 FOR ATTRIBUTE BYTES
	ADD	DI,AX			; ADD LOCATION TO START OF REGEN PAGE
	MOV	DX,@ADDR_6845		; GET BASE ADDRESS OF ACTIVE DISPLAY
	ADD	DX,6			;  DX= STATUS PORT ADDRESS OF ADAPTER
	RET				;  BP= ATTRIBUTE/CHARACTER (FROM BL/AL)
					;  DI= POSITION (OFFSET IN REGEN BUFFER)
FIND_POSITION	ENDP			;  BL= MODE FLAG (ZERO FOR 80X25 COLOR)
PAGE
;-------------------------------------------------------------------------------
; WRITE_AC_CURRENT							       :
;	THTS ROUTINE WRITES THE ATTRIBUTE AND CHARACTER 		       :
;	AT THE CURRENT CURSOR POSITION					       :
; INPUT 								       :
;	(AH) = CURRENT CRT MODE 					       :
;	(BH) = DISPLAY PAGE						       :
;	(CX) = COUNT OF CHARACTERS TO WRITE				       :
;	(AL) = CHAR TO WRITE						       :
;	(BL) = ATTRIBUTE OF CHAR TO WRITE				       :
;	(DS) = DATA SEGMENT						       :
;	(ES) = REGEN SEGMENT						       :
; OUTPUT								       :
;	DISPLAY REGEN BUFFER UPDATED					       :
;-------------------------------------------------------------------------------

WRITE_AC_CURRENT	PROC	NEAR
	CMP	AH,4			; IS THIS GRAPHICS
	JC	P30
	CMP	AH,7			; IS THIS BW CARD
	JE	P30
	JMP	GRAPHICS_WRITE
P30:					;  WRITE_AC_CONTINUE
	CALL	FIND_POSITION		; GET REGEN LOCATION AND PORT ADDRESS
					; ADDRESS IN (DI) REGISTER
	OR	BL,BL			; CHECK MODE FLAG FOR COLOR CARD AT 80
	JZ	P32			; SKIP TO RETRACE WAIT IF COLOR AT 80

	XCHG	AX,BP			; GET THE ATTR/CHAR SAVED FOR FAST WRITE
	REP	STOSW			; STRING WRITE THE ATTRIBUTE A CHARACTER
	JMP	SHORT  P35		; EXIT FAST WRITE ROUTINE

;-----	WAIT FOR HORIZONTAL RETRACE OR VERTICAL RETRACE IF COLOR 80

P31:					;  LOOP FOR EACH ATTR/CHAR WRITE
	XCHG	BP,AX			; PLACE ATTR/CHAR BACK IN SAVE REGISTER
P32:					;  WAIT FOR HORZ RETRACE LOW OR VERTICAL
	STI				; ENABLE INTERRUPTS FIRST
	NOP				; ALLOW FOR INTERRUPT WINDOW
	CLI				; BLOCK INTERRUPTS FOR SINGLE LOOP
	IN	AL,DX			; GET STATUS FROM THE ADAPTER
	TEST	AL,RVRT 		; CHECK FOR VERTICAL RETRACE FIRST
	JNZ	P34			; DO FAST WRITE NOW IF VERTICAL RETRACE
	TEST	AL,RHRZ 		; HORIZONTAL RETRACE LOW THEN
	JNZ	P32			; WAIT UNTIL IT IS
P33:					; WAIT FOR EITHER RETRACE HIGH
	IN	AL,DX			; GET STATUS AGAIN
	TEST	AL,RVRT+RHRZ		; IS HORIZONTAL OR VERTICAL RETRACE HIGH
	JZ	P33			; WAIT UNTIL EITHER IS ACTIVE
P34:
	XCHG	AX,BP			; GET THE ATTR/CHAR SAVED IN (BP)
	STOSW				; WRITE THE ATTRIBUTE AND CHARACTER
	LOOP	P31			; AS MANY TIMES AS REQUESTED - TILL CX=0
P35:
	JMP	VIDEO_RETURN		; EXIT

WRITE_AC_CURRENT	ENDP

;-------------------------------------------------------------------------------
; WRITE_C_CURRENT							       :
;	THIS ROUTINE WRITES THE CHARACTER AT				       :
;	THE CURRENT CURSOR POSITION, ATTRIBUTE UNCHANGED		       :
; INPUT 								       :
;	(AH) = CURRENT CRT MODE 					       :
;	(BH) = DISPLAY PAGE						       :
;	(CX) = COUNT OF CHARACTERS TO WRITE				       :
;	(AL) = CHAR TO WRITE						       :
;	(DS) = DATA SEGMENT						       :
;	(ES) = REGEN SEGMENT						       :
; OUTPUT								       :
;	DISPLAY REGEN BUFFER UPDATED					       :
;-------------------------------------------------------------------------------

WRITE_C_CURRENT PROC	NEAR
	CMP	AH,4			; IS THIS GRAPHICS
	JC	P40
	CMP	AH,7			; IS THIS BW CARD
	JE	P40
	JMP	GRAPHICS_WRITE
P40:
	CALL	FIND_POSITION		; GET REGEN LOCATION AND PORT ADDRESS
					; ADDRESS OF LOCATION IN (DI)

;-----	WAIT FOR HORIZONTAL RETRACE OR VERTICAL RETRACE IF COLOR 80

P41:					;  WAIT FOR HORZ RETRACE LOW OR VERTICAL
	STI				; ENABLE INTERRUPTS FIRST
	OR	BL,BL			; CHECK MODE FLAG FOR COLOR CARD IN 80
	JNZ	P43			; ELSE SKIP RETRACE WAIT - DO FAST WRITE
	CLI				; BLOCK INTERRUPTS FOR SINGLE LOOP
	IN	AL,DX			; GET STATUS FROM THE ADAPTER
	TEST	AL,RVRT 		; CHECK FOR VERTICAL RETRACE FIRST
	JNZ	P43			; DO FAST WRITE NOW IF VERTICAL RETRACE
	TEST	AL,RHRZ 		; IS HORIZONTAL RETRACE LOW THEN
	JNZ	P41			; WAIT UNTIL IT IS
P42:					;  WAIT FOR EITHER RETRACE HIGH
	IN	AL,DX			; GET STATUS AGAIN
	TEST	AL,RVRT+RHRZ		; IS HORIZONTAL OR VERTICAL RETRACE HIGH
	JZ	P42			; WAIT UNTIL EITHER RETRACE ACTIVE
P43:
	MOV	AX,BP			; GET THE CHARACTER SAVE IN (BP)
	STOSB				; PUT THE CHARACTER INTO REGEN BUFFER
	INC	DI			; BUMP POINTER PAST ATTRIBUTE
	LOOP	P41			;  AS MANY TIMES AS REQUESTED

	JMP	VIDEO_RETURN

WRITE_C_CURRENT ENDP
PAGE
;-------------------------------------------------------------------------------
; WRITE_STRING								       :
;	THIS ROUTINE WRITES A STRING OF CHARACTERS TO THE CRT.		       :
; INPUT 								       :
;	(AL) = WRITE STRING COMMAND  0 - 3				       :
;	(BH) = DISPLAY PAGE (ACTIVE PAGE)				       :
;	(CX) = COUNT OF CHARACTERS TO WRITE, IF (CX) = 0 THEN RETURN	       :
;	(DX) = CURSOR POSITION FOR START OF STRING WRITE		       :
;	(BL) = ATTRIBUTE OF CHARACTER TO WRITE IF (AL) = 0  OR	(AL) = 1       :
;	(BP) = SOURCE STRING OFFSET					       :
;	[0E] = SOURCE STRING SEGMENT (FOR USE IN (ES) IN STACK +14)	       :
; OUTPUT								       :
;	NONE								       :
;-------------------------------------------------------------------------------
WRITE_STRING	PROC	NEAR
	PUSH	BP			; SAVE BUFFER OFFSET (BP) IN STACK
	MOV	BP,SP			; GET POINTER TO STACKED REGISTERS
	MOV	ES,[BP]+14+2		; RECOVER ENTRY (ES) SEGMENT REGISTER
	POP	BP			; RESTORE BUFFER OFFSET
	CBW				; CLEAR (AH) REGISTER
	MOV	DI,AX			; SAVE (AL) COMMAND IN (DI) REGISTER
	CMP	AL,04			; TEST FOR INVALID WRITE STRING OPTION
	JNB	P59			; IF OPTION INVALID THEN RETURN

	JCXZ	P59			; IF ZERO LENGTH STRING THEN RETURN

	MOV	SI,BX			; GET CURRENT CURSOR PAGE
	MOV	BL,BH			; MOVE PAGE TO LOW BYTE
	XOR	BH,BH			; CLEAR HIGH BYTE
	XCHG	SI,BX			; MOVE OFFSET AND RESTORE PAGE REGISTER
	SAL	SI,1			; CONVERT TO PAGE OFFSET  (SI= PAGE)
	PUSH	[SI+OFFSET @CURSOR_POSN]; SAVE CURRENT CURSOR POSITION IN STACK
	MOV	AX,0200H		; SET NEW CURSOR POSITION
	INT	10H
P50:
	MOV	AL,ES:[BP]		; GET CHARACTER FROM INPUT STRING
	INC	BP			; BUMP POINTER TO CHARACTER

;-----	TEST FOR SPECIAL CHARACTER'S

	CMP	AL,08H			; IS IT A BACKSPACE
	JE	P51			; BACK_SPACE
	CMP	AL,CR			; IS IT CARRIAGE RETURN
	JE	P51			; CAR_RET
	CMP	AL,LF			; IS IT A LINE FEED
	JE	P51			; LINE_FEED
	CMP	AL,07H			; IS IT A BELL
	JNE	P52			; IF NOT THEN DO WRITE CHARACTER
P51:
	MOV	AH,0EH			; TTY_CHARACTER_WRITE
	INT	10H			    ; WRITE TTY CHARACTER TO THE CRT
	MOV	DX,[SI+OFFSET @CURSOR_POSN] ; GET CURRENT CURSOR POSITION
	JMP	SHORT P54		    ; SET CURSOR POSITION AND CONTINUE
P52:
	PUSH	CX
	PUSH	BX
	MOV	CX,1			; SET CHARACTER WRITE AMOUNT TO ONE
	CMP	DI,2			; IS THE ATTRIBUTE IN THE STRING
	JB	P53			; IF NOT THEN SKIP
	MOV	BL,ES:[BP]		; ELSE GET NEW ATTRIBUTE
	INC	BP			; BUMP STRING POINTER
P53:
	MOV	AH,09H			;  GOT_CHARACTER
	INT	10H			; WRITE CHARACTER TO THE CRT
	POP	BX			; RESTORE REGISTERS
	POP	CX
	INC	DL			; INCREMENT COLUMN COUNTER
	CMP	DL,BYTE PTR @CRT_COLS	; IF COLS ARE WITHIN RANGE FOR THIS MODE
	JB	P54			;    THEN GO TO COLUMNS SET
	INC	DH			; BUMP ROW COUNTER BY ONE
	SUB	DL,DL			; SET COLUMN COUNTER TO ZERO
	CMP	DH,25			; IF ROWS ARE LESS THAN 25 THEN
	JB	P54			;    GO TO ROWS_COLUMNS_SET

	MOV	AX,0E0AH		; ELSE SCROLL SCREEN
	INT	10H			; RESET ROW COUNTER TO 24
	DEC	DH
P54:					;  ROW_COLUMNS_SET
	MOV	AX,0200H		; SET NEW CURSOR POSITION COMMAND
	INT	10H			; ESTABLISH NEW CURSOR POSITION
	LOOP	P50			; DO IT ONCE MORE UNTIL (CX) = ZERO

	POP	DX			; RESTORE OLD CURSOR COORDINATES
	XCHG	AX,DI			; RECOVER WRITE STRING COMMAND
	TEST	AL,01H			; IF CURSOR WAS NOT TO BE MOVED THEN
	JNZ	P59			;  THEN EXIT WITHOUT RESETTING OLD VALUE
	MOV	AX,0200H		; ELSE RESTORE OLD CURSOR POSITION
	INT	10H
P59:					;  DONE - EXIT WRITE STRING
	JMP	VIDEO_RETURN		; RETURN TO CALLER

WRITE_STRING	ENDP
PAGE
;--------------------------------------------
; READ DOT -- WRITE DOT
; THESE ROUTINES WILL WRITE A DOT, OR READ THE
;  DOT AT THE INDICATED LOCATION
; ENTRY --
;   DX = ROW (0-199)	(THE ACTUAL VALUE DEPENDS ON THE MODE)
;   CX = COLUMN ( 0-639) ( THE VALUES ARE NOT RANGE CHECKED )
;   AL = DOT VALUE TO WRITE (1,2 OR 4 BITS DEPENDING ON MODE,
;	REQUIRED FOR WRITE DOT ONLY, RIGHT JUSTIFIED)
;	BIT 7 OF AL = 1 INDICATES XOR THE VALUE INTO THE LOCATION
;   DS = DATA SEGMENT
;   ES = REGEN SEGMENT
;
; EXIT
;	AL = DOT VALUE READ, RIGHT JUSTIFIED, READ ONLY
;----------------------------------------------
	ASSUME	DS:DATA,ES:DATA
READ_DOT	PROC	NEAR
	CALL	R3			; DETERMINE BYTE POSITION OF DOT
	MOV	AL,ES:[SI]		; GET THE BYTE
	AND	AL,AH			; MASK OFF THE OTHER BITS IN THE BYTE
	SHL	AL,CL			; LEFT JUSTIFY THE VALUE
	MOV	CL,DH			; GET NUMBER OF BITS IN RESULT
	ROL	AL,CL			; RIGHT JUSTIFY THE RESULT
	JMP	VIDEO_RETURN		; RETURN FROM VIDEO I/O
READ_DOT	ENDP

WRITE_DOT	PROC	NEAR
	PUSH	AX			; SAVE DOT VALUE
	PUSH	AX			;  TWICE
	CALL	R3			; DETERMINE BYTE POSITION OF THE DOT
	SHR	AL,CL			; SHIFT TO SET UP THE BITS FOR OUTPUT
	AND	AL,AH			; STRIP OFF THE OTHER BITS
	MOV	CL,ES:[SI]		; GET THE CURRENT BYTE
	POP	BX			; RECOVER XOR FLAG
	TEST	BL,80H			; IS IT ON
	JNZ	R2			; YES, XOR THE DOT
	NOT	AH			; SET MASK TO REMOVE THE INDICATED BITS
	AND	CL,AH
	OR	AL,CL			; OR IN THE NEW VALUE OF THOSE BITS
R1:					; FINISH_DOT
	MOV	ES:[SI],AL		; RESTORE THE BYTE IN MEMORY
	POP	AX
	JMP	VIDEO_RETURN		; RETURN FROM VIDEO I/O
R2:					; XOR_DOT
	XOR	AL,CL			; EXCLUSIVE OR THE DOTS
	JMP	R1			; FINISH UP THE WRITING
WRITE_DOT	ENDP
;----------------------------------------------
; THIS SUBROUTINE DETERMINES THE REGEN BYTE LOCATION OF THE
; INDICATED ROW COLUMN VALUE IN GRAPHICS MODE.
; ENTRY --
;  DX = ROW VALUE (0-199)
;  CX = COLUMN VALUE (0-639)
; EXIT --
;  SI = OFFSET INTO REGEN BUFFER FOR BYTE OF INTEREST
;  AH = MASK TO STRIP OFF THE BITS OF INTEREST
;  CL = BITS TO SHIFT TO RIGHT JUSTIFY THE MASK IN AH
;  DH = # BITS IN RESULT
;  BX = MODIFIED
;-----------------------------------------------
R3	PROC	NEAR

;-----	DETERMINE 1ST BYTE IN INDICATED ROW BY MULTIPLYING ROW VALUE BY 40
;-----	 ( LOW BIT OF ROW DETERMINES EVEN/ODD, 80 BYTES/ROW )

	XCHG	SI,AX			; WILL SAVE AL AND AH DURING OPERATION
	MOV	AL,40
	MUL	DL			; AX= ADDRESS OF START OF INDICATED ROW
	TEST	AL,008H 		; TEST FOR EVEN/ODD ROW CALCULATED
	JZ	R4			; JUMP IF EVEN ROW
	ADD	AX,2000H-40		; OFFSET TO LOCATION OF ODD ROWS ADJUST
R4:					; EVEN_ROW
	XCHG	SI,AX			; MOVE POINTER TO (SI) AND RECOVER (AX)
	MOV	DX,CX			; COLUMN VALUE TO DX

;-----	DETERMINE GRAPHICS MODE CURRENTLY IN EFFECT

; SET UP THE REGISTERS ACCORDING TO THE MODE
; CH = MASK FOR LOW OF COLUMN ADDRESS ( 7/3 FOR HIGH/MED RES )
; CL = # OF ADDRESS BITS IN COLUMN VALUE ( 3/2 FOR H/M )
; BL = MASK TO SELECT BITS FROM POINTED BYTE ( 80H/C0H FOR H/M )
; BH = NUMBER OF VALID BITS IN POINTED BYTE ( 1/2 FOR H/M )

	MOV	BX,2C0H
	MOV	CX,302H 		; SET PARMS FOR MED RES
	CMP	@CRT_MODE,6
	JC	R5			; HANDLE IF MED RES
	MOV	BX,180H
	MOV	CX,703H 		; SET PARMS FOR HIGH RES

;-----	DETERMINE BIT OFFSET IN BYTE FROM COLUMN MASK
R5:
	AND	CH,DL			; ADDRESS OF PEL WITHIN BYTE TO CH

;-----	DETERMINE BYTE OFFSET FOR THIS LOCATION IN COLUMN

	SHR	DX,CL			; SHIFT BY CORRECT AMOUNT
	ADD	SI,DX			; INCREMENT THE POINTER
	MOV	DH,BH			; GET THE # OF BITS IN RESULT TO DH

;-----	MULTIPLY BH (VALID BITS IN BYTE) BY CH (BIT OFFSET)

	SUB	CL,CL			; ZERO INTO STORAGE LOCATION
R6:
	ROR	AL,1			; LEFT JUSTIFY VALUE IN AL (FOR WRITE)
	ADD	CL,CH			; ADD IN THE BIT OFFSET VALUE
	DEC	BH			; LOOP CONTROL
	JNZ	R6			; ON EXIT, CL HAS COUNT TO RESTORE BITS
	MOV	AH,BL			;  GET MASK TO AH
	SHR	AH,CL			;  MOVE THE MASK TO CORRECT LOCATION
	RET				;  RETURN WITH EVERYTHING SET UP
R3	ENDP
;------------------------------------------------------
;  SCROLL UP
;   THIS ROUTINE SCROLLS UP THE INFORMATION ON THE CRT
; ENTRY ---
;  CH,CL = UPPER LEFT CORNER OF REGION TO SCROLL
;  DH,DL = LOWER RIGHT CORNER OF REGION TO SCROLL
;   BOTH OF THE ABOVE ARE IN CHARACTER POSITIONS
;  BH = FILL VALUE FOR BLANKED LINES
;  AL = # LINES TO SCROLL (AL=0 MEANS BLANK THE ENTIRE FIELD)
;  DS = DATA SEGMENT
;  ES = REGEN SEGMENT
; EXIT --
;  NOTHING, THE SCREEN IS SCROLLED
;--------------------------------------------------------
GRAPHICS_UP	PROC	NEAR
	MOV	BL,AL			; SAVE LINE COUNT IN BL
	MOV	AX,CX			; GET UPPER LEFT POSITION INTO AX REG

;-----	USE CHARACTER SUBROUTINE FOR POSITIONING
;-----	ADDRESS RETURNED IS MULTIPLIED BY 2 FROM CORRECT VALUE

	CALL	GRAPH_POSN
	MOV	DI,AX			; SAVE RESULT AS DESTINATION ADDRESS

;-----	DETERMINE SIZE OF WINDOW

	SUB	DX,CX
	ADD	DX,101H 		; ADJUST VALUES
	SAL	DH,1			; MULTIPLY ROWS BY 4 AT 8 VERT DOTS/CHAR
	SAL	DH,1			;  AND EVEN/ODD ROWS

;-----	DETERMINE CRT MODE

	CMP	@CRT_MODE,6		; TEST FOR MEDIUM RES
	JNC	R7			; FIND_SOURCE

;-----	MEDIUM RES UP
	SAL	DL,1			; # COLUMNS * 2, SINCE 2 BYTES/CHAR
	SAL	DI,1			; OFFSET *2 SINCE 2 BYTES/CHAR

;-----	DETERMINE THE SOURCE ADDRESS IN THE BUFFER
R7:					; FIND_SOURCE
	PUSH	ES			; GET SEGMENTS BOTH POINTING TO REGEN
	POP	DS
	SUB	CH,CH			; ZERO TO HIGH OF COUNT REGISTER
	SAL	BL,1			; MULTIPLY NUMBER OF LINES BY 4
	SAL	BL,1
	JZ	R11			; IF ZERO, THEN BLANK ENTIRE FIELD
	MOV	AL,80			; 80 BYTES/ROW
	MUL	BL			; DETERMINE OFFSET TO SOURCE
	MOV	SI,DI			; SET UP SOURCE
	ADD	SI,AX			;  ADD IN OFFSET TO IT
	MOV	AH,DH			; NUMBER OF ROWS IN FIELD
	SUB	AH,BL			; DETERMINE NUMBER TO MOVE

;-----	LOOP THROUGH, MOVING ONE ROW AT A TIME, BOTH EVEN AND ODD FIELDS
R8:					; ROW_LOOP
	CALL	R17			; MOVE ONE ROW
	SUB	SI,2000H-80		; MOVE TO NEXT ROW
	SUB	DI,2000H-80
	DEC	AH			; NUMBER OF ROWS TO MOVE
	JNZ	R8			; CONTINUE TILL ALL MOVED

;-----	FILL IN THE VACATED LINE(S)
R9:					; CLEAR ENTRY
	MOV	AL,BH			; ATTRIBUTE TO FILL WITH
R10:
	CALL	R18			; CLEAR THAT ROW
	SUB	DI,2000H-80		; POINT TO NEXT LINE
	DEC	BL			; NUMBER OF LINES TO FILL
	JNZ	R10			; CLEAR LOOP
	JMP	VIDEO_RETURN		; EVERYYHING DONE

R11:					; BLANK_FIELD
	MOV	BL,DH			; SET BLANK COUNT TO EVERYTHING IN FIELD
	JMP	R9			; CLEAR THE FIELD
GRAPHICS_UP	ENDP
;------------------------------------------------------
; SCROLL DOWN
;  THIS ROUTINE SCROLLS DOWN THE INFORMATION ON THE CRT
; ENTRY --
;  CH,CL = UPPER LEFT CORNER OF REGION TO SCROLL
;  DH,DL = LOWER RIGHT CORNER OF REGION TO SCROLL
;   BOTH OF THE ABOVE ARE IN CHARACTER POSITIONS
;  BH = FILL VALUE FOR BLANKED LINES
;  AL = # LINES TO SCROLL (AL=0 MEANS BLANK THE ENTIRE FIELD)
;  DS = DATA SEGMENT
;  ES = REGEN SEGMENT
; EXIT --
;  NOTHING, THE SCREEN IS SCROLLED
;--------------------------------------------------------

GRAPHICS_DOWN	PROC	NEAR
	STD				; SET DIRECTION
	MOV	BL,AL			; SAVE LINE COUNT IN BL
	MOV	AX,DX			; GET LOWER RIGHT POSITION INTO AX REG

;-----	USE CHARACTER SUBROUTINE FOR POSITIONING
;-----	ADDRESS RETURNED IS MULTIPLIED BY 2 FROM CORRECT VALUE

	CALL	GRAPH_POSN
	MOV	DI,AX			; SAVE RESULT AS DESTINATION ADDRESS

;-----	DETERMINE SIZE OF WINDOW

	SUB	DX,CX
	ADD	DX,101H 		; ADJUST VALUES
	SAL	DH,1			; MULTIPLY ROWS BY 4 AT 8 VERT DOTS/CHAR
	SAL	DH,1			;  AND EVEN/ODD ROWS

;-----	DETERMINE CRT MODE

	CMP	@CRT_MODE,6		; TEST FOR MEDIUM RES
	JNC	R12			; FIND_SOURCE_DOWN

;-----	MEDIUM RES DOWN
	SAL	DL,1			; # COLUMNS * 2, SINCE 2 BYTES/CHAR
	SAL	DI,1			; OFFSET *2 SINCE 2 BYTES/CHAR
	INC	DI			; POINT TO LAST BYTE

;-----	DETERMINE THE SOURCE ADDRESS IN THE BUFFER
R12:					; FIND_SOURCE_DOWN
	PUSH	ES			; BOTH SEGMENTS TO REGEN
	POP	DS
	SUB	CH,CH			; ZERO TO HIGH OF COUNT REGISTER
	ADD	DI,240			; POINT TO LAST ROW OF PIXELS
	SAL	BL,1			; MULTIPLY NUMBER OF LINES BY 4
	SAL	BL,1			;
	JZ	R16			; IF ZERO, THEN BLANK ENTIRE FIELD
	MOV	AL,80			; 80 BYTES/ROW
	MUL	BL			; DETERMINE OFFSET TO SOURCE
	MOV	SI,DI			; SET UP SOURCE
	SUB	SI,AX			;  SUBTRACT THE OFFSET
	MOV	AH,DH			; NUMBER OF ROWS IN FIELD
	SUB	AH,BL			; DETERMINE NUMBER TO MOVE

;-----	LOOP THROUGH, MOVING ONE ROW AT A TIME, BOTH EVEN AND ODD FIELDS
R13:					; ROW_LOOP_DOWN
	CALL	R17			; MOVE ONE ROW
	SUB	SI,2000H+80		; MOVE TO NEXT ROW
	SUB	DI,2000H+80
	DEC	AH			; NUMBER OF ROWS TO MOVE
	JNZ	R13			; CONTINUE TILL ALL MOVED

;-----	FILL IN THE VACATED LINE(S)
R14:					; CLEAR_ENTRY_DOWN
	MOV	AL,BH			; ATTRIBUTE TO FILL WITH
R15:					; CLEAR_LOOP_DOWN
	CALL	R18			; CLEAR A ROW
	SUB	DI,2000H+80		; POINT TO NEXT LINE
	DEC	BL			; NUMBER OF LINES TO FILL
	JNZ	R15			; CLEAR_LOOP_DOWN

	JMP	VIDEO_RETURN		; EVERYTHING DONE

R16:					; BLANK_FIELD_DOWN
	MOV	BL,DH			; SET BLANK COUNT TO EVERYTHING IN FIELD
	JMP	R14			; CLEAR THE FIELD
GRAPHICS_DOWN	ENDP

;-----	ROUTINE TO MOVE ONE ROW OF INFORMATION

R17	PROC	NEAR
	MOV	CL,DL			; NUMBER OF BYTES IN THE ROW
	PUSH	SI
	PUSH	DI			; SAVE POINTERS
	REP	MOVSB			; MOVE THE EVEN FIELD
	POP	DI
	POP	SI
	ADD	SI,2000H
	ADD	DI,2000H		; POINT TO THE ODD FIELD
	PUSH	SI
	PUSH	DI			; SAVE THE POINTERS
	MOV	CL,DL			; COUNT BACK
	REP	MOVSB			; MOVE THE ODD FIELD
	POP	DI
	POP	SI			; POINTERS BACK
	RET				; RETURN TO CALLER
R17	ENDP

;-----	CLEAR A SINGLE ROW

R18	PROC	NEAR
	MOV	CL,DL			; NUMBER OF BYTES IN FIELD
	PUSH	DI			; SAVE POINTER
	REP	STOSB			; STORE THE NEW VALUE
	POP	DI			; POINTER BACK
	ADD	DI,2000H		; POINT TO ODD FIELD
	PUSH	DI
	MOV	CL,DL
	REP	STOSB			; FILL THE ODD FIELD
	POP	DI
	RET				; RETURN TO CALLER
R18	ENDP
;--------------------------------------------------
; GRAPHICS WRITE
;  THIS ROUTINE WRITES THE ASCII CHARACTER TO THE CURRENT
;  POSITION ON THE SCREEN.
; ENTRY --
;  AL = CHARACTER TO WRITE
;  BL = COLOR ATTRIBUTE TO BE USED FOR FOREGROUND COLOR
;	IF BIT 7 IS SET, THE CHAR IS XOR'D INTO THE REGEN BUFFER
;	(0 IS USED FOR THE BACKGROUND COLOR)
;  CX = NUMBER OF CHARS TO WRITE
;  DS = DATA SEGMENT
;  ES = REGEN SEGMENT
; EXIT --
;  NOTHING IS RETURNED
;
; GRAPHICS READ
;  THIS ROUTINE READS THE ASCII CHARACTER AT THE CURRENT CURSOR
;  POSITION ON THE SCREEN BY MATCHING THE DOTS ON THE SCREEN TO THE
;  CHARACTER GENERATOR CODE POINTS
; ENTRY --
;  NONE (0 IS ASSUMED AS THE BACKGROUND COLOR)
; EXIT --
;  AL = CHARACTER READ AT THAT POSITION (0 RETURNED IF NONE FOUND)
;
; FOR BOTH ROUTINES, THE IMAGES USED TO FORM CHARS ARE CONTAINED IN ROM
;  FOR THE 1ST 128 CHARS.  TO ACCESS CHARS IN THE SECOND HALF, THE USER
;  MUST INITIALIZE THE VECTOR AT INTERRUPT 1FH (LOCATION 0007CH) TO
;  POINT TO THE USER SUPPLIED TABLE OF GRAPHIC IMAGES (8X8 BOXES).
;  FAILURE TO DO SO WILL CAUSE IN STRANGE RESULTS
;-----------------------------------------------------
	ASSUME	DS:DATA,ES:DATA
GRAPHICS_WRITE	PROC	NEAR
	MOV	AH,0			; ZERO TO HIGH OF CODE POINT
	PUSH	AX			; SAVE CODE POINT VALUE

;-----	DETERMINE POSITION IN REGEN BUFFER TO PUT CODE POINTS

	CALL	S26			; FIND LOCATION IN REGEN BUFFER
	MOV	DI,AX			; REGEN POINTER IN DI

;-----	DETERMINE REGION TO GET CODE POINTS FROM

	POP	AX			; RECOVER CODE POINT
	CMP	AL,80H			; IS IT IN SECOND HALF
	JAE	S1			; YES

;-----	IMAGE IS IN FIRST HALF, CONTAINED IN ROM

	MOV	SI,OFFSET CRT_CHAR_GEN	; OFFSET OF IMAGES
	PUSH	CS			; SAVE SEGMENT ON STACK
	JMP	SHORT S2		; DETERMINE_MODE

;-----	IMAGE IS IN SECOND HALF, IN USER MEMORY

S1:					; EXTEND_CHAR
	SUB	AL,80H			; ZERO ORIGIN FOR SECOND HALF
	PUSH	DS			; SAVE DATA POINTER
	SUB	SI,SI
	MOV	DS,SI			; ESTABLISH VECTOR ADDRESSING
	ASSUME	DS:ABS0
	LDS	SI,@EXT_PTR		; GET THE OFFSET OF THE TABLE
	MOV	DX,DS			; GET THE SEGMENT OF THE TABLE
	ASSUME	DS:DATA
	POP	DS			; RECOVER DATA SEGMENT
	PUSH	DX			; SAVE TABLE SEGMENT ON STACK
	OR	DX,SI			; CHECK FOR VALID TABLE DEFINED
	JNZ	S2			; CONTINUE IF DS:SI NOT 0000:0000

	POP	AX			; ELSE SET (AX)= 0000 FOR "NULL"
	MOV	SI,OFFSET CRT_CHAR_GEN	; POINT TO DEFAULT TABLE OFFSET
	PUSH	CS			;  IN THE CODE SEGMENT

;-----	DETERMINE GRAPHICS MODE IN OPERATION

S2:					;	DETERMINE_MODE
	SAL	AX,1			; MULTIPLY CODE POINT VALUE BY 8
	SAL	AX,1
	SAL	AX,1
	ADD	SI,AX			; SI HAS OFFSET OF DESIRED CODES
	CMP	@CRT_MODE,6
	POP	DS			; RECOVER TABLE POINTER SEGMENT
	JC	S7			; TEST FOR MEDIUM RESOLUTION MODE

;-----	HIGH RESOLUTION MODE
S3:					; HIGH_CHAR
	PUSH	DI			; SAVE REGEN POINTER
	PUSH	SI			; SAVE CODE POINTER
	MOV	DH,4			; NUMBER OF TIMES THROUGH LOOP
S4:
	LODSB				; GET BYTE FROM CODE POINTS
	TEST	BL,80H			; SHOULD WE USE THE FUNCTION
	JNZ	S6			;  TO PUT CHAR IN
	STOSB				; STORE IN REGEN BUFFER
	LODSB
S5:
	MOV	ES:[DI+2000H-1],AL	; STORE IN SECOND HALF
	ADD	DI,79			; MOVE TO NEXT ROW IN REGEN
	DEC	DH			; DONE WITH LOOP
	JNZ	S4
	POP	SI
	POP	DI			; RECOVER REGEN POINTER
	INC	DI			; POINT TO NEXT CHAR POSITION
	LOOP	S3			; MORE CHARS TO WRITE
	JMP	VIDEO_RETURN

S6:
	XOR	AL,ES:[DI]		; EXCLUSIVE OR WITH CURRENT
	STOSB				; STORE THE CODE POINT
	LODSB				; AGAIN FOR ODD FIELD
	XOR	AL,ES:[DI+2000H-1]
	JMP	S5			; BACK TO MAINSTREAM

;-----	MEDIUM RESOLUTION WRITE
S7:					;  MED_RES_WRITE
	MOV	DL,BL			; SAVE HIGH COLOR BIT
	SAL	DI,1			; OFFSET*2 SINCE 2 BYTES/CHAR
					; EXPAND BL TO FULL WORD OF COLOR
	AND	BL,3			; ISOLATE THE COLOR BITS ( LOW 2 BITS )
	MOV	AL,055H 		; GET BIT CONVERSION MULTIPLIER
	MUL	BL			; EXPAND 2 COLOR BITS TO 4 REPLICATIONS
	MOV	BL,AL			; PLACE BACK IN WORK REGISTER
	MOV	BH,AL			; EXPAND TO 8 REPLICATIONS OF COLOR BITS
S8:					;  MED_CHAR
	PUSH	DI			; SAVE REGEN POINTER
	PUSH	SI			; SAVE THE CODE POINTER
	MOV	DH,4			; NUMBER OF LOOPS
S9:
	LODSB				; GET CODE POINT
	CALL	S21			; DOUBLE UP ALL THE BITS
	AND	AX,BX			; CONVERT TO FOREGROUND COLOR ( 0 BACK )
	XCHG	AH,AL			; SWAP HIGH/LOW BYTES FOR WORD MOVE
	TEST	DL,80H			; IS THIS XOR FUNCTION
	JZ	S10			; NO, STORE IT IN AS IS
	XOR	AX,ES:[DI]		; DO FUNCTION WITH LOW/HIGH
S10:
	MOV	ES:[DI],AX		; STORE FIRST BYTE HIGH, SECOND LOW
	LODSB				; GET CODE POINT
	CALL	S21
	AND	AX,BX			; CONVERT TO COLOR
	XCHG	AH,AL			; SWAP HIGH/LOW BYTES FOR WORD MOVE
	TEST	DL,80H			; AGAIN, IS THIS XOR FUNCTION
	JZ	S11			; NO, JUST STORE THE VALUES
	XOR	AX,ES:[DI+2000H]	; FUNCTION WITH FIRST HALF LOW
S11:
	MOV	ES:[DI+2000H],AX	; STORE SECOND PORTION HIGH
	ADD	DI,80			; POINT TO NEXT LOCATION
	DEC	DH
	JNZ	S9			; KEEP GOING
	POP	SI			; RECOVER CODE POINTER
	POP	DI			; RECOVER REGEN POINTER
	INC	DI			; POINT TO NEXT CHAR POSITION
	INC	DI
	LOOP	S8			; MORE TO WRITE
	JMP	VIDEO_RETURN
GRAPHICS_WRITE	ENDP
;----------------------------------------
; GRAPHICS READ
;----------------------------------------
GRAPHICS_READ	PROC	NEAR
	CALL	S26			; CONVERTED TO OFFSET IN REGEN
	MOV	SI,AX			; SAVE IN SI
	SUB	SP,8			; ALLOCATE SPACE FOR THE READ CODE POINT
	MOV	BP,SP			; POINTER TO SAVE AREA

;-----	DETERMINE GRAPHICS MODES

	CMP	@CRT_MODE,6
	PUSH	ES
	POP	DS			; POINT TO REGEN SEGMENT
	JC	S13			; MEDIUM RESOLUTION

;-----	HIGH RESOLUTION READ

;-----	GET VALUES FROM REGEN BUFFER AND CONVERT TO CODE POINT
	MOV	DH,4			; NUMBER OF PASSES
S12:
	MOV	AL,[SI] 		; GET FIRST BYTE
	MOV	[BP],AL 		; SAVE IN STORAGE AREA
	INC	BP			; NEXT LOCATION
	MOV	AL,[SI+2000H]		; GET LOWER REGION BYTE
	MOV	[BP],AL 		; ADJUST AND STORE
	INC	BP
	ADD	SI,80			; POINTER INTO REGEN
	DEC	DH			; LOOP CONTROL
	JNZ	S12			; DO IT SOME MORE
	JMP	SHORT S15		; GO MATCH THE SAVED CODE POINTS

;-----	MEDIUM RESOLUTION READ
S13:
	SAL	SI,1			; OFFSET*2 SINCE 2 BYTES/CHAR
	MOV	DH,4			; NUMBER OF PASSES
S14:
	CALL	S23			; GET BYTES FROM REGEN INTO SINGLE SAVE
	ADD	SI,2000H-2		; GO TO LOWER REGION
	CALL	S23			; GET THIS PAIR INTO SAVE
	SUB	SI,2000H-80+2		; ADJUST POINTER BACK INTO UPPER
	DEC	DH
	JNZ	S14			; KEEP GOING UNTIL ALL 8 DONE

;-----	SAVE AREA HAS CHARACTER IN IT, MATCH IT
S15:					; FIND_CHAR
	MOV	DI,OFFSET CRT_CHAR_GEN	; ESTABLISH ADDRESSING
	PUSH	CS
	POP	ES			; CODE POINTS IN CS
	SUB	BP,8			; ADJUST POINTER TO START OF SAVE AREA
	MOV	SI,BP
	MOV	AL,0			; CURRENT CODE POINT BEING MATCHED
S16:
	PUSH	SS			; ESTABLISH ADDRESSING TO STACK
	POP	DS			; FOR THE STRING COMPARE
	MOV	DX,128			; NUMBER TO TEST AGAINST
S17:
	PUSH	SI			; SAVE SAVE AREA POINTER
	PUSH	DI			; SAVE CODE POINTER
	MOV	CX,4			; NUMBER OF WORDS TO MATCH
	REPE	CMPSW			; COMPARE THE 8 BYTES AS WORDS
	POP	DI			; RECOVER THE POINTERS
	POP	SI
	JZ	S18			; IF ZERO FLAG SET, THEN MATCH OCCURRED
	INC	AL			; NO MATCH, MOVE ON TO NEXT
	ADD	DI,8			; NEXT CODE POINT
	DEC	DX			; LOOP CONTROL
	JNZ	S17			; DO ALL OF THEM

;-----	CHAR NOT MATCHED, MIGHT BE IN USER SUPPLIED SECOND HALF

	CMP	AL,0			;  AL<> 0 IF ONLY 1ST HALF SCANNED
	JE	S18			;  IF = 0, THEN ALL HAS BEEN SCANNED
	SUB	AX,AX
	MOV	DS,AX			;  ESTABLISH ADDRESSING TO VECTOR
	ASSUME	DS:ABS0
	LES	DI,@EXT_PTR		;  GET POINTER
	MOV	AX,ES			;  SEE IF THE POINTER REALLY EXISTS
	OR	AX,DI			; IF ALL 0, THEN DOESN'T EXIST
	JZ	S18			; NO SENSE LOOKING
	MOV	AL,128			; ORIGIN FOR SECOND HALF
	JMP	S16			; GO BACK AND TRY FOR IT
	ASSUME	DS:DATA

;-----	CHARACTER IS FOUND ( AL=0 IF NOT FOUND )
S18:
	ADD	SP,8			; READJUST THE STACK, THROW AWAY SAVE
	JMP	VIDEO_RETURN		; ALL DONE
GRAPHICS_READ	ENDP
;--------------------------------------------
; EXPAND BYTE
;  THIS ROUTINE TAKES THE BYTE IN AL AND DOUBLES ALL
;  OF THE BITS, TURNING THE 8 BITS INTO 16 BITS.
;  THE RESULT IS LEFT IN AX
;--------------------------------------------
S21	PROC	NEAR
	PUSH	CX			; SAVE REGISTER
	MOV	CX,8			; SHIFT COUNT REGISTER FOR ONE BYTE
S22:
	ROR	AL,1			; SHIFT BITS, LOW BIT INTO CARRY FLAG
	RCR	BP,1			; MOVE CARRY FLAG (LOW BIT INTO RESULTS
	SAR	BP,1			; SIGN EXTEND HIGH BIT (DOUBLE IT)
	LOOP	S22			; REPEAT FOR ALL 8 BITS

	XCHG	AX,BP			; MOVE RESULTS TO PARAMETER REGISTER
	POP	CX			; RECOVER REGISTER
	RET				; ALL DONE
S21	ENDP
;--------------------------------------------------
; MED_READ_BYTE
; THIS ROUTINE WILL TAKE 2 BYTES FROM THE REGEN BUFFER,
;  COMPARE AGAINST THE CURRENT FOREGROUND COLOR, AND PLACE
;  THE CORRESPONDING ON/OFF BIT PATTERN INTO THE CURRENT
;  POSITION IN THE SAVE AREA
; ENTRY --
;  SI,DS = POINTER TO REGEN AREA OF INTEREST
;  BX = EXPANDED FOREGROUND COLOR
;  BP = POINTER TO SAVE AREA
; EXIT --
;  SI AND BP ARE INCREMENTED
;----------------------------------------------------
S23	PROC	NEAR
	LODSW				; GET FIRST BYTE AND SECOND BYTES
	XCHG	AL,AH			; SWAP FOR COMPARE
	MOV	CX,0C000H		; 2 BIT MASK TO TEST THE ENTRIES
	MOV	DL,0			; RESULT REGISTER
S24:
	TEST	AX,CX			; IS THIS SECTION BACKCROUND?
	JZ	S25			; IF ZERO, IT IS BACKGROUND (CARRY=0)
	STC				; WASN'T, SO SET CARRY
S25:
	RCL	DL,1			; MOVE THAT BIT INTO THE RESULT
	SHR	CX,1
	SHR	CX,1			; MOVE THE MASK TO THE RIGHT BY 2 BITS
	JNC	S24			; DO IT AGAIN IF MASK DIDN'T FALL OUT
	MOV	[BP],DL 		; STORE RESULT IN SAVE AREA
	INC	BP			; ADJUST POINTER
	RET				; ALL DONE
S23	ENDP
;-----------------------------------------
; V4_POSITION
;  THIS ROUTINE TAKES THE CURSOR POSITION CONTAINED IN
;  THE MEMORY LOCATION, AND CONVERTS IT INTO AN OFFSET
;  INTO THE REGEN BUFFER, ASSUMING ONE BYTE/CHAR.
;  FOR MEDIUM RESOLUTION GRAPHICS, THE NUMBER MUST
;  BE DOUBLED.
; ENTRY -- NO REGISTERS,MEMORY LOCATION @CURSOR_POSN IS USED
; EXIT--
;  AX CONTAINS OFFSET INTO REGEN BUFFER
;-----------------------------------------
S26	PROC	NEAR
	MOV	AX,@CURSOR_POSN 	; GET CURRENT CURSOR
GRAPH_POSN	LABEL	NEAR
	PUSH	BX			; SAVE REGISTER
	MOV	BX,AX			; SAVE A COPY OF CURRENT CURSOR
	MOV	AL,BYTE PTR @CRT_COLS	; GET BYTES PER COLUMN
	MUL	AH			; MULTIPLY BY ROWS
	SHL	AX,1
	SHL	AX,1			; MULTIPLY * 4 SINCE 4 ROWS/BYTE
	SUB	BH,BH			; ISOLATE COLUMN VALUE
	ADD	AX,BX			; DETERMINE OFFSET
	POP	BX			; RECOVER POINTER
	RET				; ALL DONE
S26	ENDP
;--- WRITE_TTY -----------------------------------------------------------------
;									       :
;   THIS INTERFACE PROVIDES A TELETYPE LIKE INTERFACE TO THE		       :
;     VIDEO CARDS.  THE INPUT CHARACTER IS WRITTEN TO THE CURRENT	       :
;     CURSOR POSITION, AND THE CURSOR IS MOVED TO THE NEXT POSITION.	       :
;     IF THE CURSOR LEAVES THE LAST COLUMN OF THE FIELD, THE COLUMN	       :
;     IS SET TO ZERO, AND THE ROW VALUE IS INCREMENTED.  IF THE ROW	       :
;     ROW VALUE LEAVES THE FIELD, THE CURSOR IS PLACED ON THE LAST ROW,        :
;     FIRST COLUMN, AND THE ENTIRE SCREEN IS SCROLLED UP ONE LINE.	       :
;     WHEN THE SCREEN IS SCROLLED UP, THE ATTRIBUTE FOR FILLING THE	       :
;     NEWLY BLANKED LINE IS READ FROM THE CURSOR POSITION ON THE PREVIOUS      :
;     LINE BEFORE THE SCROLL, IN CHARACTER MODE.  IN GRAPHICS MODE,	       :
;     THE 0 COLOR IS USED.						       :
;   ENTRY --								       :
;     (AH) = CURRENT CRT MODE						       :
;     (AL) = CHARACTER TO BE WRITTEN					       :
;	     NOTE THAT BACK SPACE, CARRIAGE RETURN, BELL AND LINE FEED ARE     :
;	     HANDLED AS COMMANDS RATHER THAN AS DISPLAY GRAPHICS CHARACTERS    :
;     (BL) = FOREGROUND COLOR FOR CHAR WRITE IF CURRENTLY IN A GRAPHICS MODE   :
;   EXIT --								       :
;      ALL REGISTERS SAVED THROUGH VIDEO_EXIT (INCLUDING (AX))		       :
;-------------------------------------------------------------------------------
	ASSUME	DS:DATA
WRITE_TTY	PROC	NEAR
	XCHG	DI,AX			; SAVE (AX) REGISTER IN (DI) FOR EXIT
	MOV	AH,03H			; READ CURSOR POSITION
	MOV	BH,@ACTIVE_PAGE 	; GET CURRENT PAGE SETTING
	INT	10H			; READ THE CURRENT CURSOR POSITION
	MOV	AX,DI			; RECOVER CHARACTER FROM (DI) REGISTER

;-----	DX NOW HAS THE CURRENT CURSOR POSITION

	CMP	AL,CR			; IS IT CARRIAGE RETURN OR CONTROL
	JBE	U8			; GO TO CONTROL CHECKS IF IT IS

;-----	WRITE THE CHAR TO THE SCREEN
U0:
	MOV	AH,0AH			; WRITE CHARACTER ONLY COMMAND
	MOV	CX,1			; ONLY ONE CHARACTER
	INT	10H			; WRITE THE CHARACTER

;-----	POSITION THE CURSOR FOR NEXT CHAR

	INC	DL
	CMP	DL,BYTE PTR @CRT_COLS	; TEST FOR COLUMN OVERFLOW
	JNZ	U7			;  SET_CURSOR
	MOV	DL,0			; COLUMN FOR CURSOR
	CMP	DH,25-1 		; CHECK FOR LAST ROW
	JNZ	U6			;  SET_CURSOR_INC

;-----	SCROLL REQUIRED
U1:
	MOV	AH,02H
	INT	10H			; SET THE CURSOR

;-----	DETERMINE VALUE TO FILL WITH DURING SCROLL

	MOV	AL,@CRT_MODE		; GET THE CURRENT MODE
	CMP	AL,4
	JC	U2			; READ-CURSOR
	CMP	AL,7
	MOV	BH,0			; FILL WITH BACKGROUND
	JNE	U3			; SCROLL-UP
U2:					; READ-CURSOR
	MOV	AH,08H			; GET READ CURSOR COMMAND
	INT	10H			; READ CHAR/ATTR AT CURRENT CURSOR
	MOV	BH,AH			; STORE IN BH
U3:
	MOV	AX,0601H		; SCROLL ONE LINE
	SUB	CX,CX			; UPPER LEFT CORNER
	MOV	DH,25-1 		; LOWER RIGHT ROW
	MOV	DL,BYTE PTR @CRT_COLS	; LOWER RIGHT COLUMN
	DEC	DL
U4:					; VIDEO-CALL-RETURN
	INT	10H			; SCROLL UP THE SCREEN
U5:					; TTY-RETURN
	XCHG	AX,DI			; RESTORE THE ENTRY CHARACTER FROM (DI)
	JMP	VIDEO_RETURN		; RETURN TO CALLER

U6:					; SET-CURSOR-INC
	INC	DH			; NEXT ROW
U7:					; SET-CURSOR
	MOV	AH,02H
	JMP	U4			; ESTABLISH THE NEW CURSOR

;-----	CHECK FOR CONTROL CHARACTERS
U8:
	JE	U9			; WAS IT A CARRIAGE RETURN
	CMP	AL,LF			; IS IT A LINE FEED
	JE	U10			; GO TO LINE FEED
	CMP	AL,07H			; IS IT A BELL
	JE	U11			; GO TO BELL
	CMP	AL,08H			; IS IT A BACKSPACE
	JNE	U0			; IF NOT A CONTROL, DISPLAY IT

;-----	BACK SPACE FOUND

	OR	DL,DL			; IS IT ALREADY AT START OF LINE
	JE	U7			; SET_CURSOR
	DEC	DX			; NO -- JUST MOVE IT BACK
	JMP	U7			; SET_CURSOR

;-----	CARRIAGE RETURN FOUND

U9:
	MOV	DL,0			; MOVE TO FIRST COLUMN
	JMP	U7			; SET-CURSOR

;-----	LINE FEED FOUND

U10:
	CMP	DH,25-1 		; BOTTOM OF SCREEN
	JNE	U6			; YES, SCROLL THE SCREEN
	JMP	U1			; NO, JUST SET THE CURSOR

;-----	BELL FOUND

U11:
	MOV	CX,1331 		; DIVISOR FOR 896 HZ TONE
	MOV	BL,31			; SET COUNT FOR 31/64 SECOND FOR BEEP
	CALL	BEEP			; SOUND THE POD BELL
	JMP	U5			; TTY_RETURN
WRITE_TTY	ENDP
;-------------------------------------------------------------------------------
; LIGHT PEN								       :
;	THIS ROUTINE TESTS THE LIGHT PEN SWITCH AND THE LIGHT		       :
;	PEN TRIGGER. IF BOTH ARE SET, THE LOCATION OF THE LIGHT 	       :
;	PEN IS DETERMINED. OTHERWISE, A RETURN WITH NO INFORMATION	       :
;	IS MADE.							       :
; ON EXIT:								       :
;	(AH) = 0 IF NO LIGHT PEN INFORMATION IS AVAILABLE		       :
;		BX,CX,DX ARE DESTROYED					       :
;	(AH) = 1 IF LIGHT PEN IS AVAILABLE				       :
;		(DH,DL) = ROW,COLUMN OF CURRENT LIGHT PEN POSITION	       :
;		(CH) = RASTER POSITION					       :
;		(BX) = BEST GUESS AT PIXEL HORIZONTAL POSITION		       :
;-------------------------------------------------------------------------------
	ASSUME DS:DATA
V1	DB	3,3,5,5,3,3,3,4 	; SUBTRACT_TABLE
;-----	WAIT FOR LIGHT PEN TO BE DEPRESSED

READ_LPEN	PROC	NEAR
	MOV	AH,0			; SET NO LIGHT PEN RETURN CODE
	MOV	DX,@ADDR_6845		; GET BASE ADDRESS OF 6845
	ADD	DX,6			; POINT TO STATUS REGISTER
	IN	AL,DX			; GET STATUS REGISTER
	TEST	AL,004H 		; TEST LIGHT PEN SWITCH
	JZ	V6_A			; GO IF YES
	JMP	V6			; NOT SET, RETURN

;-----	NOW TEST FOR LIGHT PEN TRIGGER

V6_A:	TEST	AL,2			; TEST LIGHT PEN TRIGGER
	JNZ	V7A			; RETURN WITHOUT RESETTING TRIGGER
	JMP	V7

;-----	TRIGGER HAS BEEN SET, READ THE VALUE IN

V7A:
	MOV	AH,16			; LIGHT PEN REGISTERS ON 6845

;-----	INPUT REGISTERS POINTED TO BY AH, AND CONVERT TO ROW COLUMN IN (DX)

	MOV	DX,@ADDR_6845		; ADDRESS REGISTER FOR 6845
	MOV	AL,AH			; REGISTER TO READ
	OUT	DX,AL			; SET IT UP
	NOP				; I/O DELAY
	INC	DX			; DATA REGISTER
	IN	AL,DX			; GET THE VALUE
	MOV	CH,AL			; SAVE IN CX
	DEC	DX			; ADDRESS REGISTER
	INC	AH
	MOV	AL,AH			; SECOND DATA REGISTER
	OUT	DX,AL
	INC	DX			; POINT TO DATA REGISTER
	NOP				; I/O DELAY
	IN	AL,DX			; GET SECOND DATA VALUE
	MOV	AH,CH			; AX HAS INPUT VALUE

;-----	AX HAS THE VALUE READ IN FROM THE 6845

	MOV	BL,@CRT_MODE
	SUB	BH,BH			; MODE VALUE TO BX
	MOV	BL,CS:V1[BX]		; DETERMINE AMOUNT TO SUBTRACT
	SUB	AX,BX			; TAKE IT AWAY
	MOV	BX,@CRT_START
	SHR	BX,1
	SUB	AX,BX			; CONVERT TO CORRECT PAGE ORIGIN
	JNS	V2			; IF POSITIVE, DETERMINE MODE
	SUB	AX,AX			; <0 PLAYS AS 0

;-----	DETERMINE MODE OF OPERATION

V2:					; DETERMINE-MOOE
	MOV	CL,3			; SET *8 SHIFT COUNT
	CMP	@CRT_MODE,4		; DETERMINE IF GRAPHICS OR ALPHA
	JB	V4			; ALPHA_PEN
	CMP	@CRT_MODE,7
	JE	V4			; ALPHA_PEN

;-----	GRAPHICS MODE

	MOV	DL,40			; DIVISOR FOR GRAPHICS
	DIV	DL			; DETERMINE ROW(AL) AND COLUMN(AH)
					;  AL RANGE 0-99, AH RANGE 0-39
;-----	DETERMINE GRAPHIC ROW POSITION

	MOV	CH,AL			; SAVE ROW VALUE IN CH
	ADD	CH,CH			; *2 FOR EVEN/ODD FIELD
	MOV	BL,AH			; COLUMN VALUE TO BX
	SUB	BH,BH			; MULTIPLY BY 8 FOR MEDIUM RES
	CMP	@CRT_MODE,6		; DETERMINE MEDIUM OR HIGH RES
	JNE	V3			; NOT_HIGH_RES
	MOV	CL,4			; SHIFT VALUE FOR HIGH RES
	SAL	AH,1			; COLUMN VALUE TIMES 2 FOR HIGH RES
V3:					; NOT_HIGH_RES
	SHL	BX,CL			; MULTIPLY *16 FOR HIGH RES

;-----	DETERMINE ALPHA CHAR POSITION

	MOV	DL,AH			; COLUMN VALUE FOR RETURN
	MOV	DH,AL			; ROW VALUE
	SHR	DH,1			; DIVIDE BY 4
	SHR	DH,1			;    FOR VALUE IN 0-24 RANGE
	JMP	SHORT V5		; LIGHT_PEN_RETURN_SET

;-----	ALPHA MODE ON LIGHT PEN

V4:					; ALPHA_PEN
	DIV	BYTE PTR @CRT_COLS	; DETERMINE ROW,COLUMN VALUE
	MOV	DH,AL			; ROWS TO DH
	MOV	DL,AH			; COLS TO DL
	SAL	AL,CL			; MULTIPLY ROWS * 8
	MOV	CH,AL			; GET RASTER VALUE TO RETURN REGISTER
	MOV	BL,AH			; COLUMN VALUE
	XOR	BH,BH			;  TO BX
	SAL	BX,CL
V5:					; LIGHT_PEN_RETURN_SET
	MOV	AH,1			; INDICATE EVERY THING SET
V6:					; LIGHT_PEN_RETURN
	PUSH	DX			; SAVE RETURN VALUE (IN CASE)
	MOV	DX,@ADDR_6845		; GET BASE ADDRESS
	ADD	DX,7			; POINT TO RESET PARM
	OUT	DX,AL			; ADDRESS, NOT DATA, IS IMPORTANT
	POP	DX			; RECOVER VALUE
V7:					; RETURN_NO_RESET
	POP	BP
	POP	DI
	POP	SI
	POP	DS			; DISCARD SAVED BX,CX,DX
	POP	DS
	POP	DS
	POP	DS
	POP	ES
	IRET
READ_LPEN	ENDP
CODE	ENDS
	END
